Beispiel #1
0
 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
Beispiel #2
0
 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")