예제 #1
0
파일: mixer.py 프로젝트: janpsx/bartendro
    def liquid_level_test(self, dispenser, threshold):
        if app.globals.get_state() == fsm.STATE_ERROR:
            return
        if not app.options.use_liquid_level_sensors: return

        log.info("Start liquid level test: (disp %s thres: %d)" %
                 (dispenser, threshold))

        if not self.driver.update_liquid_levels():
            raise BartendroBrokenError("Failed to update liquid levels")
        sleep(.01)

        level = self.driver.get_liquid_level(dispenser)
        log.info("initial reading: %d" % level)
        if level <= threshold:
            log.info("liquid is out before starting: %d" % level)
            return

        last = -1
        self.driver.start(dispenser)
        while level > threshold:
            if not self.driver.update_liquid_levels():
                raise BartendroBrokenError("Failed to update liquid levels")
                return
            sleep(.01)
            level = self.driver.get_liquid_level(dispenser)
            if level != last:
                log.info("  %d" % level)
            last = level

        self.driver.stop(dispenser)
        log.info("Stopped at level: %d" % level)
        sleep(.1)
        level = self.driver.get_liquid_level(dispenser)
        log.info("motor stopped at level: %d" % level)
예제 #2
0
    def _state_pre_shot(self):

        if not app.options.use_liquid_level_sensors:
            return fsm.EVENT_LL_OK

        try:
            ll = self._check_liquid_levels()
        except BartendroLiquidLevelReadError:
            raise BartendroBrokenError("Failed to read liquid levels")

        booze_id = self.recipe.data.keys()[0]
        dispensers = db.session.query(Dispenser).order_by(Dispenser.id).all()
        for i, disp in enumerate(dispensers):
            if disp.booze_id == booze_id:
                if disp.out == LL_OUT:
                    if ll == LL_OK:
                        app.globals.set_state(fsm.STATE_OK)
                    elif ll == LL_LOW:
                        app.globals.set_state(fsm.STATE_LOW)
                    elif ll == LL_OUT:
                        app.globals.set_state(fsm.STATE_OUT)
                    else:
                        app.globals.set_state(fsm.STATE_HARD_OUT)

                    raise BartendroCantPourError(
                        "Cannot make drink: Dispenser %d is out of booze." %
                        (i + 1))
                break

        return fsm.EVENT_LL_OK
예제 #3
0
 def sync(self, state):
     if self.software_only: return
     try:
         if (state):
             self._write_byte_with_retry(ROUTER_ADDRESS, ROUTER_CMD_SYNC_ON)
         else:
             self._write_byte_with_retry(ROUTER_ADDRESS,
                                         ROUTER_CMD_SYNC_OFF)
     except IOError:
         app.globals.set_state(fsm.STATE_ERROR)
         raise BartendroBrokenError("Cannot write to I2C interface.")
예제 #4
0
 def _write_byte_with_retry(self, address, byte):
     try:
         self.router.write_byte(address, byte)
     except IOError, e:
         # if we get an error, try again, just once
         try:
             log.error("*** router send: error while sending. Retrying. " +
                       repr(e))
             self.router.write_byte(address, byte)
         except IOError:
             app.globals.set_state(fsm.STATE_ERROR)
             raise BartendroBrokenError("Cannot write to I2C interface.")
예제 #5
0
    def open(self):
        '''Open the i2c connection to the router'''

        if self.software_only: return

        if smbus_missing:
            log.error("You must install the smbus module!")
            sys.exit(-1)

        log.info("Opening I2C bus to router")
        try:
            self.router = smbus.SMBus(ROUTER_BUS)
        except IOError:
            app.globals.set_state(fsm.STATE_ERROR)
            raise BartendroBrokenError("Cannot open I2C interface.")
        log.info("Done.")
예제 #6
0
    def _dispense_recipe(self, recipe, always_fast=False):

        active_disp = []
        for disp in recipe:
            if not recipe[disp]:
                continue
            ticks = int(recipe[disp] * TICKS_PER_ML)
            if recipe[disp] < SLOW_DISPENSE_THRESHOLD and not always_fast:
                speed = HALF_SPEED
            else:
                speed = FULL_SPEED
            self.driver.set_motor_direction(disp, MOTOR_DIRECTION_FORWARD)
            # todo: create dispense_ml, why do we care about 'ticks'?
            #if not self.driver.dispense_ticks(disp, ticks, speed):
            if not self.driver.dispense_ml(disp, ticks, speed):
                raise BartendroBrokenError(
                    "Dispense error. Dispense %d ml, speed %d on dispenser %d failed."
                    % (recipe[disp], speed, disp + 1))

            active_disp.append(disp)
            sleep(.01)

        for disp in active_disp:
            while True:
                (is_dispensing, over_current) = app.driver.is_dispensing(disp)
                log.debug("is_disp %d, over_cur %d" %
                          (is_dispensing, over_current))

                # If we get errors here, try again. Running motors can cause noisy comm lines
                if is_dispensing < 0 or over_current < 0:
                    log.error(
                        "Is dispensing test on dispenser %d failed. Ignoring."
                        % (disp + 1))
                    sleep(.2)
                    continue

                if over_current:
                    raise BartendroCurrentSenseError(
                        "One of the pumps did not operate properly. Your drink is broken. Sorry. :("
                    )

                if is_dispensing == 0:
                    break

                sleep(.1)
예제 #7
0
    def _state_pre_pour(self):
        try:
            ll = self._check_liquid_levels()
        except BartendroLiquidLevelReadError:
            raise BartendroBrokenError("Failed to read liquid levels")

        # update the list of drinks we can make
        drinks = self.get_available_drink_list()
        if len(drinks) == 0:
            raise BartendroCantPourError("Cannot make this drink now.")

        if ll == LL_OK:
            return fsm.EVENT_LL_OK

        if ll == LL_LOW:
            return fsm.EVENT_LL_LOW

        return LL_OUT
예제 #8
0
class Mixer(object):
    '''The mixer object is the heart of Bartendro. This is where the state of the bot
       is managed, checked if drinks can be made, and actually make drinks. Everything
       else in Bartendro lives for *this* *code*. :) '''
    def __init__(self, driver, mc):
        if driver:
            self.driver = driver
            self.mc = mc
            self.disp_count = self.driver.count()
            self.do_event(fsm.EVENT_START)
        self.err = ""

    def check_levels(self):
        with BartendroLock(app.globals):
            self.do_event(fsm.EVENT_CHECK_LEVELS)

    def dispense_shot(self, dispenser, ml):
        r = Recipe()
        r.data = {dispenser.booze.id: ml}
        r.booze = dispenser.booze
        self.recipe = r

        with BartendroLock(app.globals):
            self.do_event(fsm.EVENT_MAKE_SHOT)
            t = int(time())
            slog = ShotLog(dispenser.booze.id, t, ml)
            db.session.add(slog)
            db.session.commit()

    def dispense_ml(self, dispenser, ml):
        r = Recipe()
        r.data = {dispenser.booze.id: ml}
        r.booze = dispenser.booze
        self.recipe = r

        with BartendroLock(app.globals):
            self.do_event(fsm.EVENT_TEST_DISPENSE)

    def make_drink(self, drink, recipe):
        r = Recipe()
        r.data = recipe
        r.drink = drink
        self.recipe = r
        log.info("make_drink drink: %r reciple: %r " % (drink, recipe))
        with BartendroLock(app.globals):
            self.do_event(fsm.EVENT_MAKE_DRINK)
            if drink and drink.id:
                size = 0
                for k in recipe.keys():
                    size += recipe[k]
                t = int(time())
                dlog = DrinkLog(drink.id, t, size)
                db.session.add(dlog)
                db.session.commit()

    def do_event(self, event):
        cur_state = app.globals.get_state()

        log.info("do_event event: %r  " % (event))
        while True:
            next_state = None
            for t_state, t_event, t_next_state in fsm.transition_table:
                if t_state == cur_state and event == t_event:
                    next_state = t_next_state
                    log.info("do_event next_state: %r  " % (next_state))
                    break

            if not next_state:
                log.error("Current state %d, event %d. No next state." %
                          (cur_state, event))
                raise BartendroBrokenError(
                    "Bartendro is unable to pour drinks right now. Sorry.")
            #print "cur state: %d event: %d next state: %d" % (cur_state, event, next_state)

            try:
                if next_state == fsm.STATE_PRE_POUR:
                    event = self._state_pre_pour()
                elif next_state == fsm.STATE_CHECK:
                    event = self._state_check()
                elif next_state == fsm.STATE_PRE_SHOT:
                    event = self._state_pre_shot()
                elif next_state == fsm.STATE_READY:
                    event = self._state_ready()
                elif next_state == fsm.STATE_LOW:
                    event = self._state_low()
                elif next_state == fsm.STATE_OUT:
                    event = self._state_out()
                elif next_state == fsm.STATE_HARD_OUT:
                    event = self._state_hard_out()
                elif next_state == fsm.STATE_POURING or next_state == fsm.STATE_POUR_SHOT:
                    event = self._state_pouring()
                elif next_state == fsm.STATE_POUR_DONE:
                    event = self._state_pour_done()
                elif next_state == fsm.STATE_CURRENT_SENSE:
                    event = self._state_current_sense()
                elif next_state == fsm.STATE_ERROR:
                    event = self._state_error()
                elif next_state == fsm.STATE_TEST_DISPENSE:
                    event = self._state_test_dispense()
                else:
                    self._state_error()
                    app.globals.set_state(fsm.STATE_ERROR)
                    log.error(
                        "Current state: %d, event %d. Can't find next state." %
                        (cur_state, event))
                    raise BartendroBrokenError(
                        "Internal error. Bartendro has had one too many.")

            except BartendroBrokenError, err:
                exc_type, exc_value, exc_traceback = sys.exc_info()
                # traceback.print_tb(exc_traceback)
                self._state_error()
                app.globals.set_state(fsm.STATE_ERROR)
                raise

            except BartendroCantPourError, err:
                exc_type, exc_value, exc_traceback = sys.exc_info()
                # traceback.print_tb(exc_traceback)
                raise

            except BartendroCurrentSenseError, err:
                exc_type, exc_value, exc_traceback = sys.exc_info()
                # traceback.print_tb(exc_traceback)
                raise BartendroBrokenError(err)
예제 #9
0
    def do_event(self, event):
        cur_state = app.globals.get_state()

        log.info("do_event event: %r  " % (event))
        while True:
            next_state = None
            for t_state, t_event, t_next_state in fsm.transition_table:
                if t_state == cur_state and event == t_event:
                    next_state = t_next_state
                    log.info("do_event next_state: %r  " % (next_state))
                    break

            if not next_state:
                log.error("Current state %d, event %d. No next state." %
                          (cur_state, event))
                raise BartendroBrokenError(
                    "Bartendro is unable to pour drinks right now. Sorry.")
            #print "cur state: %d event: %d next state: %d" % (cur_state, event, next_state)

            try:
                if next_state == fsm.STATE_PRE_POUR:
                    event = self._state_pre_pour()
                elif next_state == fsm.STATE_CHECK:
                    event = self._state_check()
                elif next_state == fsm.STATE_PRE_SHOT:
                    event = self._state_pre_shot()
                elif next_state == fsm.STATE_READY:
                    event = self._state_ready()
                elif next_state == fsm.STATE_LOW:
                    event = self._state_low()
                elif next_state == fsm.STATE_OUT:
                    event = self._state_out()
                elif next_state == fsm.STATE_HARD_OUT:
                    event = self._state_hard_out()
                elif next_state == fsm.STATE_POURING or next_state == fsm.STATE_POUR_SHOT:
                    event = self._state_pouring()
                elif next_state == fsm.STATE_POUR_DONE:
                    event = self._state_pour_done()
                elif next_state == fsm.STATE_CURRENT_SENSE:
                    event = self._state_current_sense()
                elif next_state == fsm.STATE_ERROR:
                    event = self._state_error()
                elif next_state == fsm.STATE_TEST_DISPENSE:
                    event = self._state_test_dispense()
                else:
                    self._state_error()
                    app.globals.set_state(fsm.STATE_ERROR)
                    log.error(
                        "Current state: %d, event %d. Can't find next state." %
                        (cur_state, event))
                    raise BartendroBrokenError(
                        "Internal error. Bartendro has had one too many.")

            except BartendroBrokenError, err:
                exc_type, exc_value, exc_traceback = sys.exc_info()
                # traceback.print_tb(exc_traceback)
                self._state_error()
                app.globals.set_state(fsm.STATE_ERROR)
                raise

            except BartendroCantPourError, err:
                exc_type, exc_value, exc_traceback = sys.exc_info()
                # traceback.print_tb(exc_traceback)
                raise