def evaluate_and_subscribe_template(self, template, parameters, text=None): """Evaluate and subscribe template.""" if self.machine.stop_future.done(): # return a canceled future if machine is already stopping future = asyncio.Future() future.cancel() return None, future try: value, subscriptions = self._eval(template, parameters, True) except TemplateEvalError as e: value = e subscriptions = e.subscriptions except ConfigFileError: # pylint: disable-msg=try-except-raise raise 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 = self.machine.wait_for_stop() else: subscriptions.append(self.machine.wait_for_stop()) future = Util.any(subscriptions) future = asyncio.ensure_future(future) return value, future
def track_eject(self, eject_tracker: EjectTracker, already_left): """Remove one ball from count.""" ball_left = self._wait_for_ball_to_leave( ) if not already_left else None ball_activity = self.wait_for_ball_activity() # we are stable from here on eject_tracker.set_ready() count = self._entrance_count while True: if ball_left: futures = [ball_activity, ball_left] else: futures = [ball_activity] yield from Util.any(futures, loop=self.machine.clock.loop) if ball_left and ball_left.done(): ball_left = False eject_tracker.track_ball_left() self.debug_log( "Device ejected a ball. Reducing ball count by one.") self._entrance_count -= 1 count -= 1 if self._entrance_count < 0: self._entrance_count = 0 self.ball_device.log.warning("Entrance count went below 0") if ball_activity.done() and self._entrance_count > count: for _ in range(self._entrance_count - count): yield from eject_tracker.track_ball_entrance() count = self._entrance_count ball_activity = self.wait_for_ball_activity()
def _eject_ball(self, eject_request: OutgoingBall, eject_try: int) -> Generator[int, None, bool]: # inform the counter that we are ejecting now self.info_log("Ejecting ball to %s", eject_request.target) yield from self._post_ejecting_event(eject_request, eject_try) ball_eject_process = yield from self.ball_device.ball_count_handler.start_eject() try: yield from ball_eject_process.will_eject() self.debug_log("Wait for ball to leave device") # eject the ball ball_left = ball_eject_process.wait_for_ball_left() waiters = [ball_left] trigger = None tilt = None if self.ball_device.ejector: # eject on tilt if eject_request.mechanical: tilt = self.machine.events.wait_for_event("tilt") waiters.append(tilt) # wait for trigger event if eject_request.mechanical and self.ball_device.config['player_controlled_eject_event']: trigger = self.machine.events.wait_for_event( self.ball_device.config['player_controlled_eject_event']) waiters.append(trigger) elif eject_request.mechanical and self.ball_device.config['mechanical_eject']: # do nothing pass else: yield from self.ball_device.ejector.eject_one_ball(ball_eject_process.is_jammed(), eject_try) # wait until the ball has left if (self.ball_device.config['mechanical_eject'] or self.ball_device.config['player_controlled_eject_event']) and eject_request.mechanical: timeout = None else: timeout = eject_request.eject_timeout try: yield from Util.any(waiters, timeout=timeout, loop=self.machine.clock.loop) except asyncio.TimeoutError: # timeout. ball did not leave. failed yield from self.ball_device.ball_count_handler.end_eject(ball_eject_process, False) return False if (trigger and trigger.done()) or (tilt and tilt.done()): yield from self.ball_device.ejector.eject_one_ball(ball_eject_process.is_jammed(), eject_try) # TODO: add timeout here yield from ball_left self.ball_device.set_eject_state("ball_left") self.debug_log("Ball left") incoming_ball_at_target = self._add_incoming_ball_to_target(eject_request.target) result = yield from self._handle_confirm(eject_request, ball_eject_process, incoming_ball_at_target, eject_try) yield from self.ball_device.ball_count_handler.end_eject(ball_eject_process, result) return result except asyncio.CancelledError: ball_eject_process.cancel() raise
def track_eject(self, eject_tracker: EjectTracker, already_left): """Return eject_process dict.""" # count active switches while True: waiter = self.wait_for_ball_activity() try: active_switches = self._count_switches_sync() waiter.cancel() break except ValueError: yield from waiter ball_left_future = Util.ensure_future( self._wait_for_ball_to_leave(active_switches), loop=self.machine.clock.loop) if not already_left else None # all switches are stable. we are ready now eject_tracker.set_ready() jam_active_before_eject = self.is_jammed() jam_active_after_eject = False active_switches = active_switches count = len(active_switches) while True: ball_count_change = Util.ensure_future( self.wait_for_ball_count_changes(count), loop=self.machine.clock.loop) if ball_left_future: futures = [ball_count_change, ball_left_future] else: futures = [ball_count_change] yield from Util.any(futures, loop=self.machine.clock.loop) if ball_left_future and ball_left_future.done(): ball_left_future = None eject_tracker.track_ball_left() count -= 1 ball_count_change.cancel() elif ball_count_change.done(): new_count = ball_count_change.result() # check jam first if not jam_active_after_eject and not jam_active_before_eject and self.is_jammed( ): eject_tracker.track_ball_returned() jam_active_after_eject = True count += 1 if new_count > count: # TODO: add some magic to detect entrances pass if new_count > count: eject_tracker.track_unknown_balls(new_count - count) elif count > new_count: eject_tracker.track_lost_balls(count - new_count) count = new_count
def evaluate_and_subscribe(self, parameters) -> Tuple[str, asyncio.Future]: """Evaluate placeholder to string and subscribe to changes.""" f = MpfFormatter(self.machine, parameters, True) value = f.format(self.text) subscriptions = f.subscriptions if not subscriptions: future = asyncio.Future() # type: asyncio.Future elif len(subscriptions) == 1: future = subscriptions[0] else: future = Util.any(subscriptions) future = asyncio.ensure_future(future) return value, future
def evaluate_and_subscribe_template(self, template, parameters): """Evaluate and subscribe template.""" try: value, subscriptions = self._eval(template, parameters, True) except TemplateEvalError as e: value = e subscriptions = e.subscriptions if not subscriptions: future = asyncio.Future(loop=self.machine.clock.loop) elif len(subscriptions) == 1: future = subscriptions[0] else: future = Util.any(subscriptions, loop=self.machine.clock.loop) future = Util.ensure_future(future, loop=self.machine.clock.loop) return value, future
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