async def _handle_confirm(self, eject_request: OutgoingBall, ball_eject_process: EjectTracker, incoming_ball_at_target: IncomingBall, eject_try: int) -> bool: # TODO: check double eject (two balls left). can only happen when not jammed timeout = eject_request.eject_timeout self.info_log("Wait for confirm with timeout %s", timeout) confirm_future = incoming_ball_at_target.wait_for_confirm() try: await Util.first([confirm_future], timeout=timeout, cancel_others=False) except asyncio.TimeoutError: self.ball_device.set_eject_state("failed_confirm") self.info_log("Got timeout (%ss) before confirm from %s", timeout, eject_request.target) return await self._handle_late_confirm_or_missing( eject_request, ball_eject_process, incoming_ball_at_target, eject_try) 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.info_log("Got eject confirm") await self._handle_eject_success(eject_request) return True
def _handle_confirm(self, eject_request: OutgoingBall, ball_eject_process: EjectTracker, incoming_ball_at_target: IncomingBall, eject_try: int) -> 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, eject_try)) 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
def _handle_late_confirm_or_missing(self, eject_request: OutgoingBall, ball_eject_process: EjectTracker, incoming_ball_at_target: IncomingBall) -> Generator[int, None, bool]: ball_return_future = Util.ensure_future(ball_eject_process.wait_for_ball_return(), loop=self.machine.clock.loop) unknown_balls_future = Util.ensure_future(ball_eject_process.wait_for_ball_unknown_ball(), loop=self.machine.clock.loop) eject_success_future = incoming_ball_at_target.wait_for_confirm() timeout = self.ball_device.config['ball_missing_timeouts'][eject_request.target] / 1000 # assume that the ball may have skipped the target device by now incoming_ball_at_target.set_can_skip() if not eject_request.target.is_playfield(): yield from eject_request.target.ball_count_handler.wait_for_count_is_valid() if eject_success_future.done(): self.debug_log("Got eject confirm (after recounting)") yield from self._handle_eject_success(ball_eject_process, eject_request) return True else: # TODO: remove hack when moving code below yield from asyncio.sleep(0.1, loop=self.machine.clock.loop) # TODO: move this to a better location if not ball_return_future.done() and not unknown_balls_future.done() and \ eject_request.target.is_playfield(): # if target is playfield mark eject as confirmed self.debug_log("Confirming eject because target is playfield and ball did not return.") incoming_ball_at_target.ball_arrived() yield from self._handle_eject_success(ball_eject_process, eject_request) return True try: event = yield from Util.first([ball_return_future, unknown_balls_future, eject_success_future], timeout=timeout, loop=self.machine.clock.loop) except asyncio.TimeoutError: # handle lost ball incoming_ball_at_target.did_not_arrive() ball_eject_process.ball_lost() yield from self.ball_device.lost_ejected_ball(target=eject_request.target) # ball is lost but the eject is finished -> return true return True else: if event == eject_success_future: # we eventually got eject success yield from self._handle_eject_success(ball_eject_process, eject_request) return True elif event == ball_return_future: # ball returned. eject failed eject_request.already_left = False incoming_ball_at_target.did_not_arrive() ball_eject_process.ball_returned() return False elif event == unknown_balls_future: # TODO: this may be an option self.debug_log("Got unknown balls. Assuming a ball returned.") incoming_ball_at_target.did_not_arrive() ball_eject_process.ball_returned() return False else: raise AssertionError("Invalid state")
def _handle_late_confirm_or_missing(self, eject_request: OutgoingBall, ball_eject_process: EjectTracker, incoming_ball_at_target: IncomingBall, eject_try: int) -> Generator[int, None, bool]: ball_return_future = Util.ensure_future(ball_eject_process.wait_for_ball_return(), loop=self.machine.clock.loop) unknown_balls_future = Util.ensure_future(ball_eject_process.wait_for_ball_unknown_ball(), loop=self.machine.clock.loop) eject_success_future = incoming_ball_at_target.wait_for_confirm() timeout = self.ball_device.config['ball_missing_timeouts'][eject_request.target] / 1000 # if ball_eject_process.is_jammed(): # # ball returned. eject failed # eject_request.already_left = False # incoming_ball_at_target.did_not_arrive() # return False # assume that the ball may have skipped the target device by now incoming_ball_at_target.set_can_skip() if not eject_request.target.is_playfield(): yield from eject_request.target.ball_count_handler.wait_for_count_is_valid() if eject_success_future.done(): self.debug_log("Got eject confirm (after recounting)") yield from self._handle_eject_success(eject_request) return True else: if (yield from self._handle_playfield_timeout_confirm( eject_request, ball_return_future, unknown_balls_future, incoming_ball_at_target)): return True try: event = yield from Util.first([ball_return_future, unknown_balls_future, eject_success_future], timeout=timeout, loop=self.machine.clock.loop) except asyncio.TimeoutError: # handle lost ball incoming_ball_at_target.did_not_arrive() yield from self._failed_eject(eject_request, eject_try, True) yield from self.ball_device.lost_ejected_ball(target=eject_request.target) # ball is lost but the eject is finished -> return true return True else: if event == eject_success_future: # we eventually got eject success yield from self._handle_eject_success(eject_request) return True elif event == ball_return_future: # ball returned. eject failed self.debug_log("Ball returned. Eject failed.") eject_request.already_left = False incoming_ball_at_target.did_not_arrive() return False elif event == unknown_balls_future: # TODO: this may be an option self.debug_log("Got unknown balls. Assuming a ball returned.") incoming_ball_at_target.did_not_arrive() return False # throw an error if we got here raise AssertionError("Invalid state")