예제 #1
0
    def evaluate_and_subscribe_template(self, template, parameters, text=None):
        """Evaluate and subscribe template."""
        try:
            value, subscriptions = self._eval(template, parameters, True)
        except TemplateEvalError as e:
            value = e
            subscriptions = e.subscriptions
        except ValueError as e:
            raise AssertionError(
                "Failed to evaluate and subscribe template {} with parameters {}. "
                "See error above.".format(text, parameters)) from e

        if not subscriptions:
            future = asyncio.Future()
        elif len(subscriptions) == 1:
            future = subscriptions[0]
        else:
            future = Util.any(subscriptions)
        future = asyncio.ensure_future(future)
        return value, future
예제 #2
0
    def play(self, settings, context, calling_context, priority=0, **kwargs):
        """Post (delayed) events."""
        for event, s in settings.items():
            s = deepcopy(s)
            event_dict = self.machine.placeholder_manager.parse_conditional_template(
                event)

            if event_dict['condition'] and not event_dict[
                    'condition'].evaluate(kwargs):
                continue

            if event_dict['number']:
                delay = Util.string_to_ms(event_dict['number'])
                self.delay.add(callback=self._post_event,
                               ms=delay,
                               event=event_dict['name'],
                               s=s,
                               **kwargs)
            else:
                self._post_event(event_dict['name'], s, **kwargs)
예제 #3
0
    def get_complete_config(self):
        root_document = self.get_root_document()
        if self._cached_config:
            return self._cached_config

        config = self._load_document_and_subconfigs(root_document)
        if "modes" in config:
            for mode in config['modes']:
                path = os.path.join(self._root_path, "modes", mode, "config",
                                    "{}.yaml".format(mode))
                if os.path.exists(path):
                    mode_document = self.get_document(uris.from_fs_path(path))
                    mode_config = self._load_document_and_subconfigs(
                        mode_document)
                    mode_config.pop("mode", None)
                    config = Util.dict_merge(config, mode_config)

        self._cached_config = config

        return config
예제 #4
0
    def add_platform(self, name: str) -> None:
        """Make an additional hardware platform interface available to MPF.

        Args:
            name: String name of the platform to add. Must match the name of a
                platform file in the mpf/platforms folder (without the .py
                extension).
        """
        if name not in self.hardware_platforms:
            if name not in self.config['mpf']['platforms']:
                raise AssertionError("Invalid platform {}".format(name))

            try:
                hardware_platform = Util.string_to_class(self.config['mpf']['platforms'][name])
            except ImportError:     # pragma: no cover
                raise ImportError("Cannot add hardware platform {}. This is "
                                  "not a valid platform name".format(name))

            self.hardware_platforms[name] = (
                hardware_platform(self))
예제 #5
0
    def test_control_events_arguments(self):
        for device_type in self.machine.config['mpf']['device_modules']:

            device_cls = Util.string_to_class(device_type)

            config_spec = self.machine.config_validator.config_spec[
                device_cls.config_section]

            for k in config_spec:
                if not k.endswith(
                        '_events'
                ) or k == "control_events" or config_spec[k] == "ignore":
                    continue
                method_name = k[:-7]
                method = getattr(device_cls, "event_{}".format(method_name),
                                 None)
                self.assertIsNotNone(
                    method, "Method {}.event_{} is missing for {}".format(
                        device_type, method_name, k))

                sig = inspect.signature(method)

                self.assertTrue(
                    sig.parameters['self'],
                    "Method {}.{} is missing self. Actual signature: {}".
                    format(device_type, method_name, sig))

                self.assertTrue(
                    'kwargs' in sig.parameters,
                    "Method {}.{} is missing **kwargs. Actual signature: {}".
                    format(device_type, method_name, sig))

                self.assertEqual(
                    sig.parameters['kwargs'].kind, inspect._VAR_KEYWORD,
                    "Method {}.{} kwargs param is missing '**'".format(
                        device_type, method_name))

                self.assertTrue(
                    hasattr(method, "relative_priority"),
                    "Method {}.{} is missing a relative_priority. Did you apply the event_handler "
                    "decorator?".format(device_type, method_name))
예제 #6
0
파일: fast.py 프로젝트: krayon/pinball-mpf
    def _parse_driver_number(self, number):
        try:
            board_str, driver_str = number.split("-")
        except ValueError:
            total_drivers = 0
            for board_obj in self.io_boards.values():
                total_drivers += board_obj.driver_count
            try:
                index = self.convert_number_from_config(number)
            except ValueError:
                self.raise_config_error(
                    "Could not parse driver number {}. Please verify the number format is either "
                    "board-driver or driver. Driver should be an integer here."
                    .format(number), 7)

            if int(index, 16) >= total_drivers:
                raise AssertionError(
                    "Driver {} does not exist. Only {} drivers found. Driver number: {}"
                    .format(int(index, 16), total_drivers, number))

            return index

        board = int(board_str)
        driver = int(driver_str)

        if board not in self.io_boards:
            raise AssertionError(
                "Board {} does not exist for driver {}".format(board, number))

        if self.io_boards[board].driver_count <= driver:
            raise AssertionError(
                "Board {} only has {} drivers. Driver: {}".format(
                    board, self.io_boards[board].driver_count, number))

        index = 0
        for board_number, board_obj in self.io_boards.items():
            if board_number >= board:
                continue
            index += board_obj.driver_count

        return Util.int_to_hex_string(index + driver)
예제 #7
0
    def __init__(self, mc, name, config, member_cls):
        """Initialise asset pool."""
        self.machine = mc
        self.priority = None
        self.name = name
        self.config = config
        self.member_cls = member_cls
        self.loading_members = set()
        self._callbacks = set()
        self.assets = list()
        self._last_asset = None
        self._asset_sequence = deque()
        self._assets_sent = set()
        self._total_weights = 0

        if 'load' not in config:
            config['load'] = 'on_demand'

        if 'type' not in config:
            config['type'] = 'sequence'

        for asset in Util.string_to_list(
                self.config[self.member_cls.config_section]):
            try:
                name, number = asset.split('|')
                if not number:
                    number = 1
                else:
                    number = int(number)
            except ValueError:
                name = asset
                number = 1

            try:
                self.assets.append(
                    (getattr(self.machine,
                             self.member_cls.attribute)[name], number))
            except KeyError:  # pragma: no cover
                raise ValueError("No asset named {}".format(name))

        self._configure_return_asset()
예제 #8
0
    def __init__(self, mc, name, config, member_cls):
        """Initialise asset pool."""
        self.machine = mc
        self.priority = None
        self.name = name
        self.config = config
        self.member_cls = member_cls
        self.loading_members = set()
        self._callbacks = set()
        self.assets = list()
        self._last_asset = None
        self._asset_sequence = deque()
        self._assets_sent = set()
        self._total_weights = 0
        self._has_conditions = False

        if 'load' not in config:
            config['load'] = 'on_demand'

        if 'type' not in config:
            config['type'] = 'sequence'

        for asset in Util.string_to_list(
                self.config[self.member_cls.config_section]):
            asset_dict = self.machine.placeholder_manager.parse_conditional_template(
                asset, default_number=1)

            # For efficiency, track whether any assets have conditions
            if asset_dict['condition']:
                self._has_conditions = True

            try:
                self.assets.append(
                    (getattr(self.machine,
                             self.member_cls.attribute)[asset_dict['name']],
                     asset_dict['number'], asset_dict['condition']))
            except KeyError:  # pragma: no cover
                raise ValueError("No asset named {}".format(
                    asset_dict['name']))

        self._configure_return_asset()
예제 #9
0
    def setUp(self):
        # Most of the setup is done in run(). Explanation is there.
        Config._named_configs.pop('app', None)

        self._start_time = time()
        self._current_time = self._start_time
        Clock._start_tick = self._start_time
        Clock._last_tick = self._start_time
        Clock.time = self._mc_time

        # prevent sleep in clock
        Clock._max_fps = 0
        Clock._events = [[] for i in range(256)]
        self._test_started = self._start_time

        from mpf.core.player import Player
        Player.monitor_enabled = False

        mpf_config = ConfigProcessor.load_config_file(
            os.path.abspath(
                os.path.join(mpfmc.__path__[0], os.pardir,
                             self.get_options()['mcconfigfile'])), 'machine')

        machine_path = self.getAbsoluteMachinePath()

        mpf_config = load_machine_config(
            Util.string_to_list(self.get_config_file()), machine_path,
            mpf_config['mpf-mc']['paths']['config'], mpf_config)
        self.preprocess_config(mpf_config)

        self.mc = MpfMc(options=self.get_options(),
                        config=mpf_config,
                        machine_path=machine_path)

        self.patch_bcp()

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        self._start_app_as_slave()
예제 #10
0
    def _setup_bcp_connections(self, queue: QueuedEvent, **kwargs):
        """Connect to BCP servers from MPF config."""
        del kwargs
        if ('connections' not in self.machine.config['bcp']
                or not self.machine.config['bcp']['connections']):
            return

        client_connect_futures = []
        for name, settings in self.machine.config['bcp']['connections'].items(
        ):
            settings = self.machine.config_validator.validate_config(
                "bcp:connections", settings)

            self.machine.events.post('bcp_connection_attempt',
                                     name=name,
                                     host=settings['host'],
                                     port=settings['port'])
            '''event: bcp_connection_attempt
            desc: MPF is attempting to make a BCP connection.
            args:
            name: The name of the connection.
            host: The host name MPF is attempting to connect to.
            port: The TCP port MPF is attempting to connect to'''

            client = Util.string_to_class(settings['type'])(self.machine, name,
                                                            self.machine.bcp)
            client.exit_on_close = settings['exit_on_close']
            connect_future = asyncio.ensure_future(
                client.connect(settings), loop=self.machine.clock.loop)
            connect_future.add_done_callback(
                partial(self.transport.register_transport, client))
            client_connect_futures.append(connect_future)

        # block init until all clients are connected
        if client_connect_futures:
            queue.wait()
            future = asyncio.ensure_future(asyncio.wait(
                iter(client_connect_futures), loop=self.machine.clock.loop),
                                           loop=self.machine.clock.loop)
            future.add_done_callback(lambda x: queue.clear())
            future.add_done_callback(self._bcp_clients_connected)
예제 #11
0
    def parse_config(self, mode_config):
        """Parse a yaml config file and create mappings for required assets."""
        if not mode_config.get('sounds'):
            return self

        for sound_pool in mode_config.get('sound_pools', {}).values():
            for soundname in Util.string_to_list(sound_pool['sounds']):
                if soundname in self._pool_tracks and self._pool_tracks[
                        soundname] != sound_pool['track']:
                    print(
                        "ERROR: Sound {} exists in multiple pools/tracks in config {}"
                        .format(soundname, self.name))
                    return
                try:
                    self._pool_tracks[soundname] = sound_pool['track']
                except KeyError:
                    raise AttributeError(
                        "Sound pool '{}'' has no track".format(soundname))

        for soundname, sound in mode_config['sounds'].items():
            self._add_sound(sound, pool_track=self._pool_tracks.get(soundname))
예제 #12
0
    def enable(self, pulse_settings: PulseSettings,
               hold_settings: HoldSettings):
        """Enable (turn on) this driver."""
        if self.autofire:
            # If this driver is also configured for an autofire rule, we just
            # manually trigger it with the trigger_cmd and manual on ('03')
            cmd = '{}{},03'.format(self.get_trigger_cmd(), self.number)
        else:
            # Otherwise we send a full config command, trigger C1 (logic triggered
            # and drive now) switch ID 00, mode 18 (latched)

            cmd = '{}{},C1,00,18,{},{},{},{}'.format(
                self.get_config_cmd(), self.number,
                Util.int_to_hex_string(pulse_settings.duration),
                self.get_pwm_for_cmd(pulse_settings.power),
                self.get_pwm_for_cmd(hold_settings.power),
                self.get_recycle_ms_for_cmd(self.config.default_recycle,
                                            pulse_settings.duration))

        self.log.debug("Sending Enable Command: %s", cmd)
        self.send(cmd)
예제 #13
0
    def _run(self):
        yield from self.machine.events.wait_for_event("init_phase_3")
        self.check_hw_switches()
        while True:
            # wait for either a new value or a switch change
            switch_change_future = self.machine.switch_controller.wait_for_any_switch(
                switch_names=[
                    switch.name for switch in self.value_switches
                    if switch is not None
                ],
                state=2,
                ms=self.config['hw_confirm_time'])
            result = yield from Util.first(
                [switch_change_future, self._busy.wait()],
                loop=self.machine.clock.loop)
            if result == switch_change_future:
                self.check_hw_switches()
                continue

            # advance the reel until we reached our destination position
            yield from self._advance_reel_if_position_does_not_match()
예제 #14
0
파일: fast.py 프로젝트: madinounet/mpf
    def set_pulse_on_hit_rule(self, enable_switch: SwitchSettings, coil: DriverSettings):
        """Set pulse on hit rule on driver."""
        self.debug_log("Setting Pulse on hit and release HW Rule. Switch: %s,"
                       "Driver: %s", enable_switch.hw_switch.number,
                       coil.hw_driver.number)

        self._check_switch_coil_combincation(enable_switch, coil)

        driver = coil.hw_driver

        cmd = '{}{},{},{},10,{},{},00,00,{}'.format(
            driver.get_config_cmd(),
            coil.hw_driver.number,
            driver.get_control_for_cmd(enable_switch),
            enable_switch.hw_switch.number[0],
            Util.int_to_hex_string(coil.pulse_settings.duration),
            driver.get_pwm_for_cmd(coil.pulse_settings.power),
            driver.get_recycle_ms_for_cmd(coil.recycle, coil.pulse_settings.duration))

        enable_switch.hw_switch.configure_debounce(enable_switch.debounce)
        driver.set_autofire(cmd, coil.pulse_settings.duration, coil.pulse_settings.power, 0)
예제 #15
0
    def _game_starting(self, queue, **kwargs):
        """Reset the score reels when a new game starts.

        This is a queue event so it doesn't allow the game start to continue
        until it's done.

        Args:
            queue: A reference to the queue object for the game starting event.
        """
        del kwargs
        # tell the game_starting event queue that we have stuff to do
        queue.wait()

        futures = []
        for score_reel_group in self.machine.score_reel_groups:
            score_reel_group.set_value(0)
            futures.append(score_reel_group.wait_for_ready())

        future = asyncio.wait(iter(futures), loop=self.machine.clock.loop)
        future = Util.ensure_future(future, loop=self.machine.clock.loop)
        future.add_done_callback(partial(self._reels_ready, queue=queue))
예제 #16
0
    def add_platform(self, name: str) -> None:
        """Make an additional hardware platform interface available to MPF.

        Args:
        ----
            name: String name of the platform to add. Must match the name of a
                platform file in the mpf/platforms folder (without the .py
                extension).
        """
        if name not in self.hardware_platforms:
            if name in self.config['mpf']['platforms']:
                # if platform is in config load it
                try:
                    hardware_platform = Util.string_to_class(
                        self.config['mpf']['platforms'][name])
                except ImportError as e:  # pragma: no cover
                    if e.name != name:  # do not swallow unrelated errors
                        raise
                    raise ImportError(
                        "Cannot add hardware platform {}. This is "
                        "not a valid platform name".format(name))

            else:
                # check entry points
                entry_points = list(
                    iter_entry_points(group='mpf.platforms', name=name))
                if entry_points:
                    # load platform from entry point
                    self.debug_log(
                        "Loading platform %s from external entry_point", name)
                    if len(entry_points) != 1:
                        raise AssertionError(
                            "Got more than one entry point for platform {}: {}"
                            .format(name, entry_points))

                    hardware_platform = entry_points[0].load()
                else:
                    raise AssertionError("Unknown platform {}".format(name))

            self.hardware_platforms[name] = hardware_platform(self)
예제 #17
0
    def _validate_dict_or_omap(self, item_type, validation,
                               validation_failure_info, item):
        if ':' not in validation:
            self.validation_error(item, validation_failure_info,
                                  "Missing : in dict validator.")

        validators = validation.split(':')

        if item_type == "omap":
            item_dict = OrderedDict()
            if not isinstance(item, OrderedDict):
                self.validation_error(
                    item, validation_failure_info,
                    "Item is not an ordered dict. "
                    "Did you forget to add !!omap to your entry?", 7)
        elif item_type == "dict":
            item_dict = dict()
            if item == "None" or item is None:
                item = {}
            if not isinstance(item, dict):
                self.validation_error(item, validation_failure_info,
                                      "Item is not a dict.", 12)
        elif item_type == "event_handler":
            # item could be str, list, or list of dicts
            item_dict = dict()
            try:
                item = Util.event_config_to_dict(item)
            except TypeError:
                self.validation_error(item, validation_failure_info,
                                      "Could not convert item to dict", 8)
        else:
            raise AssertionError("Invalid type {}".format(item_type))

        for k, v in item.items():
            item_dict[self.validate_item(
                k, validators[0],
                validation_failure_info)] = (self.validate_item(
                    v, validators[1], ValidationPath(validation_failure_info,
                                                     k)))
        return item_dict
예제 #18
0
    def validate_item(self, item, validator, validation_failure_info):
        """Validate an item using a validator."""
        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.items():
                return_dict[self.validate_item(
                    k, validator[0],
                    validation_failure_info)] = (self.validate_item(
                        v, validator[1], validation_failure_info))

            return return_dict

        elif '(' in validator and ')' in validator[-1:] == ')':
            validator_parts = validator.split('(')
            validator = validator_parts[0]
            param = validator_parts[1][:-1]
            return self.validator_list[validator](
                item,
                validation_failure_info=validation_failure_info,
                param=param)
        elif validator in self.validator_list:
            return self.validator_list[validator](
                item, validation_failure_info=validation_failure_info)

        else:
            raise ConfigFileError(
                "Invalid Validator '{}' in config spec {}:{}".format(
                    validator, validation_failure_info[0][0],
                    validation_failure_info[1]), 4, self.log.name)
예제 #19
0
    def play(self, settings, context, calling_context, priority=0, **kwargs):
        """Set light color based on config."""
        instance_dict = self._get_instance_dict(context)
        full_context = self._get_full_context(context)
        del kwargs

        for light, s in settings.items():
            s = deepcopy(s)
            try:
                s['priority'] += priority
            except KeyError:
                s['priority'] = priority
            if isinstance(light, str):
                light_names = Util.string_to_list(light)
                for light_name in light_names:
                    # skip non-replaces placeholders
                    if not light_name or light_name[0:1] == "(" and light_name[-1:] == ")":
                        continue
                    self._light_named_color(light_name, instance_dict, full_context, s['color'], s["fade"],
                                            s['priority'])
            else:
                self._light_color(light, instance_dict, full_context, s['color'], s["fade"], s['priority'])
예제 #20
0
    def pulse(self, coil, milliseconds):
        """Pulse this driver."""
        if not self.can_be_pulsed:
            if self.use_switch:
                raise AssertionError(
                    "Cannot currently pulse driver {} because hw_rule needs hold_power"
                    .format(self.number))
            self.solCard.platform.reconfigure_driver(coil, False)

        if milliseconds and milliseconds != self.config['pulse_ms']:
            raise AssertionError(
                "OPP platform doesn't allow changing pulse width using pulse call. "
                "Tried {}, used {}".format(milliseconds,
                                           self.config['pulse_ms']))

        _, _, solenoid = self.number.split("-")
        sol_int = int(solenoid)
        self.log.debug("Pulsing solenoid %s", self.number)
        self._kick_coil(sol_int, True)

        hex_ms_string = self.config['pulse_ms']
        return Util.hex_string_to_int(hex_ms_string)
예제 #21
0
    async def initialize_devices(self):
        """Initialise devices."""
        futures = []
        for device_type in self.machine.config['mpf']['device_modules']:

            device_cls = Util.string_to_class(device_type)

            collection_name, config_name = device_cls.get_config_info()

            if config_name not in self.machine.config:
                continue

            # Get the config section for these devices
            collection = getattr(self.machine, collection_name)
            config = self.machine.config[config_name]

            # add machine wide
            for device_name in config:
                futures.append(
                    collection[device_name].device_added_system_wide())

        await asyncio.wait(futures)
예제 #22
0
    def _load_config_from_files(self) -> None:
        self.log.info("Loading config from original files")

        self.config = self._get_mpf_config()
        self.config['_mpf_version'] = __version__

        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,
                                          ConfigProcessor.load_config_file(
                                              config_file,
                                              config_type='machine'))

        if self.options['create_config_cache']:
            self._cache_config()
예제 #23
0
    def hex_to_rgb(_hex, default=None):
        """Convert a HEX color representation to an RGB color representation.

        Args:
            _hex: The 3- or 6-char hexadecimal string representing the color
                value.
            default: The default value to return if _hex is invalid.

        Returns: RGB representation of the input HEX value as a 3-item tuple
            with each item being an integer 0-255.

        """
        if not Util.is_hex_string(_hex):
            return default

        _hex = str(_hex).strip('#')

        n = len(_hex) // 3
        r = int(_hex[:n], 16)
        g = int(_hex[n:2 * n], 16)
        b = int(_hex[2 * n:3 * n], 16)
        return r, g, b
예제 #24
0
    def process_animations(self, config):
        # config is localized to the slide's 'animations' section

        for event_name, event_settings in config.items():

            # str means it's a list of named animations
            if isinstance(event_settings, str):
                event_settings = Util.string_to_list(event_settings)

            # dict means it's a single set of settings for one animation step
            elif isinstance(event_settings, dict):
                event_settings = [event_settings]

            # ultimately we're producing a list of dicts, so build that list
            # as we iterate
            new_list = list()
            for settings in event_settings:
                new_list.append(self.process_animation(settings))

            config[event_name] = new_list

        return config
예제 #25
0
    def _run(self):
        """Handle timeouts."""
        while True:
            if not self._incoming_balls:
                self._has_incoming_balls.clear()
                self._has_no_incoming_balls.set()

            # sleep until we have incoming balls
            yield from self._has_incoming_balls.wait()

            # the incoming balls may have been removed in the meantime
            if not self._incoming_balls:
                continue

            # wait for timeouts on incoming balls
            futures = [
                incoming_ball.wait_for_timeout()
                for incoming_ball in self._incoming_balls
            ]
            yield from Util.first(futures, loop=self.machine.clock.loop)

            yield from self._is_timeouting.acquire()

            # handle timeouts
            timeouts = []
            for incoming_ball in self._incoming_balls:
                if incoming_ball.is_timeouted:
                    timeouts.append(incoming_ball)

            for incoming_ball in timeouts:
                self.ball_device.log.warning(
                    "Incoming ball from %s timeouted.", incoming_ball.source)
                self._incoming_balls.remove(incoming_ball)

            for incoming_ball in timeouts:
                yield from self.ball_device.lost_incoming_ball(
                    source=incoming_ball.source)

            self._is_timeouting.release()
예제 #26
0
    def _load_device_modules(self, **kwargs):
        del kwargs
        # step 1: create devices in machine collection
        self.debug_log("Creating devices...")
        for device_type in self.machine.config['mpf']['device_modules']:
            device_cls = Util.string_to_class(device_type)  # type: Device

            collection_name, config = device_cls.get_config_info()

            self.device_classes[collection_name] = device_cls

            # create the collection
            collection = DeviceCollection(self.machine, collection_name,
                                          device_cls.config_section)

            self.collections[collection_name] = collection
            setattr(self.machine, collection_name, collection)

            # Get the config section for these devices
            config = self.machine.config.get(config, None)

            # create the devices
            if config:
                self.create_devices(collection_name, config)

            # create the default control events
            try:
                self._create_default_control_events(collection)
            except KeyError:
                pass

        self.machine.mode_controller.create_mode_devices()

        # step 2: load config and validate devices
        self.load_devices_config(validate=True)
        yield from self.machine.mode_controller.load_mode_devices()

        # step 3: initialise devices (mode devices will be initialised when mode is started)
        yield from self.initialize_devices()
예제 #27
0
    def do_load(self):
        """Load the image."""
        # This is the method that's actually called to load the asset from
        # disk. It's called by the loader thread so it's ok to block. However
        # since it's a separate thread, don't update any other attributes.

        # When you're done loading and return, the asset will be processed and
        # the various load status attributes will be updated automatically,
        # and anything that was waiting for it to load will be called. So
        # all you have to do here is load and return.

        if self.config.get('image_template'):
            try:
                template = self.machine.machine_config['image_templates'][self.config['image_template']]
                self.config = Util.dict_merge(template, self.config)
            except KeyError:
                raise KeyError("Image template '{}' was not found, referenced in image config {}".format(
                               self.config['image_template'], self.config))

        if self.machine.machine_config['mpf-mc']['zip_lazy_loading']:
            # lazy loading for zip file image sequences
            ImageLoader.zip_loader = KivyImageLoaderPatch.lazy_zip_loader

        self._image = Image(self.config['file'],
                            keep_data=False,
                            scale=1.0,
                            mipmap=False,
                            anim_delay=-1,
                            nocache=True)

        self._image.anim_reset(False)

        if self.config.get('frame_skips'):
            # Frames are provided in 1-index values, but the image animates in zero-index values
            self.frame_skips = {s['from'] - 1: s['to'] - 1 for s in self.config['frame_skips']}

        # load first texture to speed up first display
        self._callbacks.add(lambda x: self._image.texture)
예제 #28
0
    def parse_light_number_to_channels(self, number: str, subtype: str):
        """Parse light channels from number string."""
        if subtype == "gi":
            if self.machine_type == 'wpc':  # translate number to FAST GI number
                number = fast_defines.wpc_gi_map.get(str(number).upper())
            else:
                number = self.convert_number_from_config(number)

            return [{"number": number}]
        elif subtype == "matrix":
            if self.machine_type == 'wpc':  # translate number to FAST light num
                number = fast_defines.wpc_light_map.get(str(number).upper())
            else:
                number = self.convert_number_from_config(number)

            return [{"number": number}]
        elif not subtype or subtype == "led":
            # if the LED number is in <channel> - <led> format, convert it to a
            # FAST hardware number
            if '-' in str(number):
                num = str(number).split('-')
                number = Util.int_to_hex_string((int(num[0]) * 64) +
                                                int(num[1]))
            else:
                number = self.convert_number_from_config(number)
            return [
                {
                    "number": number + "-0"
                },
                {
                    "number": number + "-1"
                },
                {
                    "number": number + "-2"
                },
            ]
        else:
            raise AssertionError("Unknown subtype {}".format(subtype))
예제 #29
0
    async def get_hw_switch_states(self):
        """Return hw switch states."""
        if not self.initial_states_sent:

            if 'virtual_platform_start_active_switches' in self.machine.config:
                initial_active_switches = []
                for switch in Util.string_to_list(
                        self.machine.
                        config['virtual_platform_start_active_switches']):
                    if switch not in self.machine.switches:
                        if " " in switch:
                            self.raise_config_error(
                                "MPF no longer supports lists separated by space in "
                                "virtual_platform_start_active_switches. Please separate "
                                "switches by comma: {}.".format(switch), 1)
                        else:
                            self.raise_config_error(
                                "Switch {} used in virtual_platform_start_active_switches was not "
                                "found in switches section.".format(switch), 1)
                    initial_active_switches.append(
                        self.machine.switches[switch].hw_switch.number)

                for k in self.hw_switches:
                    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.values() if x.platform == self
            ]

            for switch in switches:
                self.hw_switches[
                    switch.hw_switch.number] = switch.state ^ switch.invert

        return self.hw_switches
 def _handle_confirm(self, eject_request: OutgoingBall, ball_eject_process: EjectTracker,
                     incoming_ball_at_target: IncomingBall) -> Generator[int, None, bool]:
     # TODO: check double eject (two balls left). can only happen when not jammed
     timeout = eject_request.eject_timeout
     self.debug_log("Wait for confirm with timeout %s", timeout)
     confirm_future = incoming_ball_at_target.wait_for_confirm()
     try:
         yield from Util.first([confirm_future], timeout=timeout,
                               loop=self.machine.clock.loop, cancel_others=False)
     except asyncio.TimeoutError:
         self.ball_device.set_eject_state("failed_confirm")
         self.debug_log("Got timeout before confirm")
         return (yield from self._handle_late_confirm_or_missing(eject_request, ball_eject_process,
                                                                 incoming_ball_at_target))
     else:
         if not confirm_future.done():
             raise AssertionError("Future not done")
         if confirm_future.cancelled():
             raise AssertionError("Eject failed but should not")
         # eject successful
         self.debug_log("Got eject confirm")
         yield from self._handle_eject_success(ball_eject_process, eject_request)
         return True