示例#1
0
def test_type():
    assert qi.Int8 == qi.Int8
    assert qi.Int8() == qi.Int8()
    assert qi.Int8 == qi.Int8()
    assert qi.Int8() == qi.Int8
    with pytest.raises(Exception):
        assert qi.Map != qi.Int8
    with pytest.raises(Exception):
        assert qi.List != qi.List(qi.Int8)
    assert qi.List(qi.Int8) == qi.List(qi.Int8)
    assert qi.List(qi.Int8()) == qi.List(qi.Int8())
    assert qi.List(qi.Int8()) == qi.List(qi.Int8)
    assert qi.List(qi.Int8) == qi.List(qi.Int8())
    assert qi.Object != qi.Int8
    assert qi.Object != qi.Int8()
    assert qi.Object != qi.Int32()
    assert qi.Int8() != qi.UInt8()
    assert (qi.Int8() != qi.Int8()) == False
    assert (qi.Int8 != qi.Int8) == False
    assert (qi.Int8 != qi.Int8()) == False
    assert (qi.Int8() != qi.Int8) == False
示例#2
0
class GFService(object):
    """Naoqi service to use Google Form for survey on Pepper"""

    # concept names
    MKEY_QUESTION = "GoogleForm/Question"
    CONCEPT_CHOICES = "choices"

    @qi.nobind
    def __init__(self, session):
        self.session = session
        self.url = ""
        self.languageDict = None
        self.formLoaded = qi.Signal("(ss)")
        self.questionLoaded = qi.Signal("(sss[s])")
        self.answerGiven = qi.Signal("(sss)")

    @qi.bind(qi.Void, paramsType=(qi.String, ))
    def loadForm(self, url):
        """Initialize a new GFParser object with url"""
        self.url = url
        self.form = PyGoogleForm.GFParser(url)
        self.questions = self.form.getQuestionIDs()
        self.questionIndex = -1
        formInfo = self.getFormInfos()
        self.formLoaded(*formInfo)

    @qi.bind(qi.Void)
    def submit(self):
        """Submit and unloads the form"""
        self.form.submit()
        self.url = ""

    @qi.bind(qi.String)
    def getFormURL(self):
        """Return the url of the current form, or empty string is no form is loaded"""
        return self.url

    @qi.bind(qi.List(qi.String))
    def getFormInfos(self):
        """Returns info [FormTitle, FormDescription]"""
        # converting to normal string as qi doesn't like list of unicodes
        return [s.encode("utf-8") for s in self.form.getFormInfos()]

    @qi.bind(qi.String)
    def nextQuestion(self):
        """Loads next question and sets up dynamic concepts. Returns question type or 'finished'"""
        self.questionIndex += 1
        if self.questionIndex >= len(self.questions):
            # fire signal!
            self.questionLoaded("", "finished", "", [])
            return "finished"
        self.questionInfo = self.form.getQuestionInfo(
            self.questions[self.questionIndex])
        if self.questionInfo[1] == "ss-checkbox":
            self.checked = []
        # Dialog settings
        self["ALMemory"].raiseEvent(self.MKEY_QUESTION,
                                    self.questionInfo[2].encode("utf-8"))
        if isinstance(self.questionInfo[3], list):
            choices = [s.encode("utf-8") for s in self.questionInfo[3]]
            self.questionInfo[3] = choices
            self["ALDialog"].setConcept(self.CONCEPT_CHOICES,
                                        self.getLanguageNU(),
                                        choices,
                                        _async=True)
        # fire signal (for tablet)
        signalInfo = self.questionInfo[:]
        if self.questionInfo[1] in ["ss-text", "ss-paragraph-text"]:
            signalInfo[3] = [signalInfo[3]]
        self.questionLoaded(*signalInfo)
        # return question type
        return self.questionInfo[1]

    @qi.bind(qi.Void, paramsType=(qi.String, ))
    def answer(self, answer):
        """answers the question"""
        self.form.answerQuestion(self.questionInfo[0], answer)
        self.answerGiven(self.questionInfo[0], self.questionInfo[1], answer)

    @qi.bind(qi.Void, paramsType=(qi.String, ))
    def addCheckbox(self, answer):
        """Adds answer to the list of checked questions"""
        self.checked.append(answer)
        self.answerGiven(self.questionInfo[0], self.questionInfo[1], answer)

    @qi.bind(qi.Void)
    def answerCheckbox(self):
        """Answers the checkbox question with list of checks"""
        self.form.answerQuestion(self.questionInfo[0], self.checked)

    @qi.nobind
    def __getitem__(self, serviceName):
        """shortcut to self.session.service"""
        return self.session.service(serviceName)

    @qi.nobind
    def getLanguageNU(self):
        """Returns Dialog language as NU style"""
        if self.languageDict is None:
            self.languageDict = self["ALDialog"].getLanguageListLongToNU()
        languageLong = self["ALDialog"].getLanguage()
        return self.languageDict[languageLong]
示例#3
0
class DarknetSRV(object):

    cocoCategories = {
        'person', 'bicycle', 'car', 'motorbike', 'aeroplane', 'bus', 'train',
        'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign',
        'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',
        'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',
        'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',
        'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard',
        'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork',
        'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',
        'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair',
        'sofa', 'pottedplant', 'bed', 'diningtable', 'toilet', 'tvmonitor',
        'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',
        'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',
        'scissors', 'teddy bear', 'hair drier', 'toothbrush'
    }

    "NAOqi service example (set/get on a simple value)."
    APP_ID = "com.aldebaran.DarknetSRV"

    def __init__(self, qiapp):
        # generic activity boilerplate
        self.qiapp = qiapp
        self.events = stk.events.EventHelper(qiapp.session)
        self.s = stk.services.ServiceCache(qiapp.session)
        self.logger = stk.logging.get_logger(qiapp.session, self.APP_ID)
        # Internal variables
        self.net = None
        self.meta = None
        self.isReady = False

        yaml_file = './config.yaml'
        with open(yaml_file) as data_file:
            config = load(data_file)

        yoloCFG = config['yoloCFG']
        yoloWeights = config['yoloWeights']
        yoloData = config['yoloData']

        #yoloCFG="/home/manolofc/workspace/darknet/cfg/yolov3.cfg"
        #yoloWeights"/home/manolofc/workspace/darknet/yolov3.weights"
        #yoloData="/home/manolofc/workspace/darknet/cfg/coco-mfc.data"

        #print("You still need to add weights, config and meta")
        self.setNet(yoloCFG, yoloWeights)
        self.setMeta(yoloData)
        self.lastImg = None
        print("Darknet service loaded")

    @qi.bind(returnType=qi.AnyArguments,
             paramsType=[qi.List(qi.UInt16)])  # == python long??
    def identify(self, img):
        "Get image return identification"
        #print("Received image")
        r = []
        if self.isReady:
            if img == None:
                print 'Error: invalid capture.'
            elif img[6] == None:
                print 'Error: no image data string.'
            else:
                #print("Image is correct")
                np_img = self.image_qi2np(img)
                cv_img = self.image_np2cv(np_img)

                # send cv image to dn
                im = self.array_to_image(cv_img)
                dn.rgbgr_image(im)
                #print("Detecting")
                r = self.detect2(self.net, self.meta, im)
                #print("Detection done")
                self.lastImg = img
        return r

    @qi.bind(returnType=qi.AnyArguments, paramsType=[qi.Void])
    def getImg(self):
        "Get used img"
        return self.lastImg

    @qi.bind(returnType=qi.Void, paramsType=[qi.String, qi.String])
    def setNet(self, yoloCFG, yoloWeights):
        "Set Net"

        if (self.net == None):
            self.net = dn.load_net(yoloCFG, yoloWeights, 0)
            print("cfg and weights added")

        self.isReady = (self.net != None) and (self.meta != None)

    @qi.bind(returnType=qi.Void, paramsType=[qi.String])
    def setMeta(self, yoloData):
        "Set meta"
        if (self.meta == None):
            print("Using yoloData file:" + yoloData)
            self.meta = dn.load_meta(yoloData)
            print("meta added")

        self.isReady = (self.net != None) and (self.meta != None)

# Private? methods

    def image_qi2np(self, dataImage):
        image = None
        if (dataImage != None):
            image = np.reshape(
                np.frombuffer(dataImage[6], dtype='%iuint8' % dataImage[2]),
                (dataImage[1], dataImage[0], dataImage[2]))
            # image = np.fromstring(str(alImage[6]), dtype=np.uint8).reshape( alImage[1],alImage[0], dataImage[2])
        return image

    def image_np2cv(self, npImage):
        # dirty way to use in cv2 or cv3
        if (cv2.__version__ == '3.3.1-dev') or (cv2.__version__ == '3.4.1'):
            open_cv_image = cv2.cvtColor(npImage, cv2.COLOR_BGR2RGB)
        else:
            open_cv_image = cv2.cvtColor(npImage, cv2.cv.CV_BGR2RGB)
        return open_cv_image

    def detect2(self, net, meta, im, thresh=.5, hier_thresh=.5, nms=.45):
        darknet2Montreal = {
            'orange': ['orange', 'cup', 'bowl', 'knife', 'fork', 'spoon'],
            'cake': ['scrubby'],
            'bottle': [
                'noodles', 'coke', 'sprite', 'sausages', 'pringles',
                'chocolate drink'
            ],
            'book': ['crackers', 'chips'],
            'cell phone': ['grape juice', 'orange juice']
        }

        num = c_int(0)
        pnum = pointer(num)
        dn.predict_image(net, im)
        dets = dn.get_network_boxes(net, im.w, im.h, thresh, hier_thresh, None,
                                    0, pnum)
        num = pnum[0]
        if (nms): dn.do_nms_obj(dets, num, meta.classes, nms)

        res = []
        for j in range(num):
            for i in range(meta.classes):
                if dets[j].prob[i] > 0:
                    b = dets[j].bbox
                    darknetName = meta.names[i]
                    if darknetName in darknet2Montreal.keys():
                        for montrealName in darknet2Montreal[darknetName]:
                            res.append((montrealName, dets[j].prob[i],
                                        (b.x, b.y, b.w, b.h)))
                    else:
                        res.append((darknetName, dets[j].prob[i], (b.x, b.y,
                                                                   b.w, b.h)))
        res = sorted(res, key=lambda x: -x[1])
        return res

    def array_to_image(self, arr):
        arr = arr.transpose(2, 0, 1)
        c = arr.shape[0]
        h = arr.shape[1]
        w = arr.shape[2]
        arr = (arr / 255.0).flatten()
        data = dn.c_array(dn.c_float, arr)
        im = dn.IMAGE(w, h, c, data)
        return im
class ALTactileGesture:
    """Class: ALTactileGesture

    Service that allows advanced touch head-sensor events to be
    detected and used by other apps via  qi.signals. Included
    gestures fire the following signal:

            - onGesture

    Hold events will repeat until the hold is released or until the sensor
    determines it has been touched for long enough (about 40s)

    IMPORTANT: ALTactileGesture module is based on qimessaging, therefore
    session.service must be used (instead of ALProxy).

    For those in need of old-style ALMemory events, you can use this:

            - ALTactileGesture/Gesture
    """
    def __init__(self, session):
        self.session = session
        logger.basicConfig(filename='ALTactileGesture.log', level=logger.DEBUG)
        self.logger = logger

        self.is_running = False
        self.module_name = 'ALTactileGesture'

        # Locks
        self.sensor_change = False
        self.sensor_lock = threading.Lock()

        # Timers
        self.s_to_micros = 1000000
        # Time from initial sensor signal to measurement of all sensors
        # (settling time)
        self.e_sim = int(0.04 * self.s_to_micros)
        # Time from sensor measurement to 'hold' status
        # (hold time)
        self.d_hold = int(0.8 * self.s_to_micros)
        # Time allowed from sensor measurement to next signal to be linked as
        # (sequence time)
        self.e_seq = int(0.2 * self.s_to_micros)
        self.e_sim_future = None
        self.d_hold_future = None
        self.e_seq_future = None

        # Build a list of devices with full name
        self.device_names = [
            '{}TactilTouched'.format(sen)
            for sen in ['Rear', 'Middle', 'Front']
        ]
        self.subscriptions = []

        # Parameters
        # Note: Below are head sensor touch sequence encoded as binary
        # representations of the three head sensors in Front, Middle, Rear order
        gestures = {
            'SingleFront': [0b000, 0b100, 0b000],
            'SingleMiddle': [0b000, 0b010, 0b000],
            'SingleRear': [0b000, 0b001, 0b000],
            'DoubleFront': [0b000, 0b100, 0b000, 0b100, 0b000],
            'DoubleMiddle': [0b000, 0b010, 0b000, 0b010, 0b000],
            'DoubleRear': [0b000, 0b001, 0b000, 0b001, 0b000],
            'SingleTap': [0b000, 0b111, 0b000],
            'DoubleTap': [0b000, 0b111, 0b000, 0b111, 0b000],
            'CaressFtoR': [0b000, 0b100, 0b010, 0b001, 0b000],
            'CaressRtoF': [0b000, 0b001, 0b010, 0b100, 0b000],
            'ZoomIn': [0b000, 0b101, 0b010, 0b000],
            'ZoomOut': [0b000, 0b010, 0b101, 0b000],
            'SecretCode': [0b000, 0b101, 0b000, 0b101, 0b010, 0b000],
            'TheClaw': [0b000, 0b101, 0b000],
            # Holds
            # Note: Hold sequences must not end in '000'
            'SingleFrontHold': [0b000, 0b100],
            'SingleMiddleHold': [0b000, 0b010],
            'SingleRearHold': [0b000, 0b001],
            'SingleTapHold': [0b000, 0b111],
            'TheClawHold': [0b000, 0b101]
        }

        # Init services
        self._connect_services()

        # Init Signals
        self._sync_preferences()
        self.all_gestures = [Gesture(k, v) for k, v in gestures.iteritems()]
        self._connect_signals()

        # Init sequence
        self.active_sequence = [0b000]
        self.active_hold = False
        self.last_sensor_state = None

        # Running process variables
        self.is_running = False

        self._start()

    def _connect_services(self):
        """Connect to all services required by ALTactileGesture"""
        self.services_connected = qi.Promise()
        services_connected_fut = self.services_connected.future()

        def get_services():
            """Attempt to get all services"""
            try:
                self.memory = self.session.service('ALMemory')
                self.prefs = self.session.service('ALPreferenceManager')
                self.logger.info('got references to all services')
                self.services_connected.setValue(True)
            except RuntimeError as e:
                self.logger.warning('missing service:\n {}'.format(e))

        get_services_task = qi.PeriodicTask()
        get_services_task.setCallback(get_services)
        get_services_task.setUsPeriod(int(2 * 1000000))  # check every 2s
        get_services_task.start(True)

        try:
            services_connected_fut.value(30 * 1000)  # timeout = 30s
            get_services_task.stop()
        except RuntimeError:
            get_services_task.stop()
            self.logger.info('Failed to reach all services after 30 seconds')
            raise RuntimeError

    def _connect_signals(self):
        """Init qi.Signals and memory events (for compatibility)"""
        self.onGesture = qi.Signal()
        self.onRelease = qi.Signal()
        self.gestureEvent = '{}/{}'.format(self.module_name, 'Gesture')
        self.releaseEvent = '{}/{}'.format(self.module_name, 'Release')
        self.memory.declareEvent(self.gestureEvent, self.module_name)
        self.memory.declareEvent(self.releaseEvent, self.module_name)

    def _on_sensor_change(self, value):
        """
        On any head sensor change, acquire lock and starts e_sim (settling)
        timer.
        Note: Only the first signal starts the timer and all others are
        debounced.
        """
        locked = self.sensor_lock.acquire(False)
        if locked:
            self._start_e_sim_timer()

    def _read_sensors(self):
        """
        Once the settling time (e_sim) is over:
          - Read from head sensors.
          - Store pattern
          - Start hold and sequential timers
        """
        # Get sensor values and convert None into 0 when necessary
        sensor_list = self.memory.getListData(self.device_names)
        raw_sensors = [0 if s is None else int(s) for s in sensor_list]
        sensor_state = 0
        # Convert sensor state to integer
        for i, b in enumerate(raw_sensors):
            sensor_state += (b << i)
        # Only act upon signal if the state of the sensors has changed
        if sensor_state != self.last_sensor_state:
            self.sensor_change = True
            self.active_sequence.append(sensor_state)
            self.last_sensor_state = sensor_state
            self._cancel_futures()
            # Start hold timer if any sensor is active
            if sensor_state:
                self._start_d_hold_timer()
            # Otherwise start sequence timer
            else:
                self._start_e_seq_timer()
        else:
            self.sensor_change = False
        self.sensor_lock.release()

    def _eval_hold(self):
        """
        Once the hold time has expired:
          - Evaluate if the current sequence is a valid hold gesture
          - Fire gesture signal (if valid)
          - Reset for next touch input
        """
        self.sensor_change = False
        if self.sensor_lock.acquire():
            if not self.sensor_change:
                self.active_hold = True
                gesture = self._search_gestures()
                if gesture:
                    self._fire_gesture_signal(gesture)
            self._clean_up_hold()
            self.sensor_lock.release()

    def _eval_sequence(self):
        """
        Once the sequence time has expired:
          - Evaluate if the current sequence is a valid gesture
          - Fire gesture signal (if valid)
          - Reset for next touch input
        """
        self.sensor_change = False
        if self.sensor_lock.acquire():
            if not self.sensor_change:
                gesture = self._search_gestures()
                if gesture:
                    self._fire_gesture_signal(gesture)
            self._fire_release_signal()
            self._clean_up()
            self.sensor_lock.release()

    def _search_gestures(self):
        """
        Compare inputted sequence to all gestures to find match

        Algorithm:
        1. async call _check_sequence on for all gestures
           (i.e. gestures that match the current hold status and are not unset custom gestures)
              -> _check_sequence() will fullfill promise with the gesture if matched; else None
        2. Build list of all futures whose value is a matched sequence
        3. Return the gesture whose match is the closest the sequence prototype
           it matched (i.e. smallest difference)
        """
        futures = []
        las = len(self.active_sequence)
        # For every sequence; start check asynchronously
        for gesture in self.all_gestures:
            if gesture.hold == self.active_hold:
                p = qi.Promise()
                futures.append(p.future())
                qi. async (self._check_sequence, self.active_sequence, las,
                           gesture, p)
        # Walks through futures list; waits on each promise to be fullfiled
        vf = (f.value() for f in futures if f.value()[0])
        # Returns gesture corresponding to min diff value in vf above
        # If vf is empty, returns (None, 0)
        return reduce(lambda x, y: min((x, y), key=op.itemgetter(1)), vf,
                      next(vf, (None, 0)))[0]

    def _check_sequence(self, a_s, las, gesture, promise):
        """
        Given a gesture, check if the active sequence matches it.

        Algorithm:
        1. Create a list of overlapping pairs from each gesture's sequence
        2. Loop through each pair (a,b):
           3. If the active sequence matches 'a' in the pair:
              4. Check if the active sequence contains the 'b' in the pair
                 5a. If True, check if it is within n-1 positions from where 'a'
                     was (Where 'n' is the number of bits changed between 'a'
                     and 'b')
                     6a. If True: goto Step 2 [if last pair; goto Step 7)
                     6b. Else: break; fullfill promise as None
                 5b. Else: break; fullfill promise as None
        7. If all pairs check out and they used all of the active sequence
           8. Fullfill promise with the gesture and the difference in length between the
              inputted sequence and the matched sequence (i.e. the number of
              excess frames)
        """
        gs = gesture.sequence
        lss = len(gs)
        # Create overlapping list of pairs
        p_seq = [(x, y) for x, y in zip(gs, gs[1:])]
        idx = 0
        idx_peek = 0
        # Walk through each pair
        for seq_tup in p_seq:
            valid = False
            # Make sure current position in a_s matches first item in pair
            if a_s[idx] == seq_tup[0]:
                try:
                    # Attempt to find second element of pair in a_s
                    idx_peek = a_s[idx:].index(seq_tup[1])
                    # Make sure it's within n-1 positions from first item in
                    # pair
                    if idx_peek <= self._bit_distance(seq_tup):
                        valid = True
                        # Update position in a_s
                        idx += idx_peek
                    else:
                        break
                except ValueError:
                    break
        # Validate that the match is proper (i.e. used all of a_s)
        if valid and idx == (las - 1):
            promise.setValue((gesture, las - lss))
            return
        # If not a match...
        promise.setValue((None, 0))

    def _bit_distance(self, pair):
        """
        Computes 'Hamming distance' between the binary representations of
        numbers in pair
        """
        i = pair[0] ^ pair[1]
        count = 0
        while i:
            i &= i - 1
            count += 1
        return count

    def _fire_gesture_signal(self, gesture):
        """Fire signal linked to gesture"""
        self.onGesture(gesture.name)
        # Raise ALMemory event for legacy support
        self.memory.raiseEvent(self.gestureEvent, gesture.name)
        qi. async (self.memory.removeData,
                   self.gestureEvent,
                   delay=self.s_to_micros)

    def _fire_release_signal(self):
        """Fire signal linked to release of sensors"""
        self.onRelease()
        # Raise ALMemory event for legacy support
        self.memory.raiseEvent(self.releaseEvent, 1)
        qi. async (self.memory.removeData,
                   self.releaseEvent,
                   delay=self.s_to_micros)

    def _clean_up(self):
        """Clean up/reset after a sequence has been completed"""
        self._cancel_futures()
        self.active_sequence = [0b000]
        self.active_hold = False
        self.sensor_change = False

    def _clean_up_hold(self):
        """Clean up/reset after a hold sequence has been completed"""
        self._cancel_futures()
        self._start_d_hold_timer()
        self.active_hold = True
        self.sensor_change = False

    def _start_e_sim_timer(self):
        """Starts timer that waits for a setteling time before reading the sensors"""
        try:
            self.e_sim_future = qi. async (self._read_sensors,
                                           delay=self.e_sim)
        except RuntimeError as re:
            self.logger.warning(
                'Error in starting settling timer: {}'.format(re))

    def _start_d_hold_timer(self):
        """Starts a timer that waits for the hold period to evaluate if there is a
        valid hold sequence"""
        try:
            self.d_hold_future = qi. async (self._eval_hold, delay=self.d_hold)
        except RuntimeError as re:
            self.logger.warning('Error in starting hold timer: {}'.format(re))

    def _start_e_seq_timer(self):
        """Starts a timer that determines if events in a sequence happen soon
        enough for them to be considered in teh current sequence."""
        try:
            self.e_seq_future = qi. async (self._eval_sequence,
                                           delay=self.e_seq)
        except RuntimeError as re:
            self.logger.warning(
                'Error in starting sequence timer: {}'.format(re))

    def _cancel_futures(self):
        """Cancel all futures"""
        try:
            if self.e_sim_future:
                self.e_sim_future.cancel()
        except RuntimeError as re:
            self.logger.warning(
                'Error in stopping settling future: {}'.format(re))
        try:
            if self.d_hold_future:
                self.d_hold_future.cancel()
        except RuntimeError as re:
            self.logger.warning('Error in stopping hold future: {}'.format(re))
        try:
            if self.e_seq_future:
                self.e_seq_future.cancel()
        except RuntimeError as re:
            self.logger.warning(
                'Error in stopping sequence future: {}'.format(re))

    def _start(self):
        """Start subscriptions to head sensors"""
        if not self.is_running:
            self.is_running = True
            # Subscribe to each sensor device
            for device in self.device_names:
                subs = self.memory.subscriber(device)
                subs_id = subs.signal.connect(self._on_sensor_change)
                self.subscriptions.append((subs, subs_id))

    def _stop(self):
        """Unsubscribe from head sensors"""
        if self.is_running:
            for subs in self.subscriptions:
                subs[0].signal.disconnect(subs[1])
            self.is_running = False

    # ############################## #
    # ############################## #
    # #### APIs and Preferences #### #
    # ############################## #
    # ############################## #

    def _sync_preferences(self):
        """Sync with preferences. This includes: Settle Time, Hold Time and Sequence Time"""
        self.logger.info('Syncing preferences...')
        # Sync Timing Preferences
        settle_time = self.prefs.getValue(self.module_name, 'SettleTime')
        if settle_time:
            self._set_settle_time(float(settle_time))
            self.logger.info('Syncing settle time...')
        hold_time = self.prefs.getValue(self.module_name, 'HoldTime')
        if hold_time:
            self._set_hold_time(float(hold_time))
            self.logger.info('Syncing hold time...')
        sequence_time = self.prefs.getValue(self.module_name, 'SequenceTime')
        if sequence_time:
            self._set_sequence_time(float(sequence_time))
            self.logger.info('Syncing sequence time...')

    @qi.bind(returnType=qi.String,
             paramsType=(qi.String, ),
             methodName='createGesture')
    def create_gesture_string(self, sensor_sequence):
        """Define touch gesture.

        :param sensor_sequence: Comma-separated string that represents
        the sequence of the desired gesture. For example, SingleFront
        would be the following: "000,100,000". NOTE: All sequences
        must start with '000' and all non-hold sequences must end with
        '000'. Hold gestures should end with the touch sequence you
        will be holding. For example, a SingleFrontHold would be the
        following: "000,100".

        :returns: If sequence is valid, the name of gesture to listen
        for, RuntimeError otherwise."""
        gs = sensor_sequence.split(',')
        return self.create_gesture_list(gs)

    @qi.bind(returnType=qi.String,
             paramsType=(qi.List(qi.String), ),
             methodName='createGesture')
    def create_gesture_list(self, sensor_sequence):
        """Define touch gesture.

        :param sensor_sequence: List of strings that represent the
        sequence of the desired gesture. For example, SingleFront
        would be the following: ['000', '100', '000']. NOTE: All
        sequences must start with '000' and all non-hold sequences
        must end with '000'. Hold gestures should end with the touch
        sequence you will be holding. For example, a SingleFrontHold
        would be the following: ['000', '100'].

        :returns: If sequence is valid, the name of gesture to listen
        for, RuntimeError otherwise."""
        if self.sensor_lock.acquire():
            self._cancel_futures()
            valid_seq = self._validate_sequence(sensor_sequence)
            if valid_seq:
                existing_seq = None
                for gesture in self.all_gestures:
                    if gesture.sequence == valid_seq:
                        existing_seq = gesture.name

                if not existing_seq:
                    gesture_name = self._create_gesture_name(valid_seq)
                    gesture = Gesture(gesture_name, valid_seq)
                    self.all_gestures.append(gesture)

            self._clean_up()
            self.sensor_lock.release()

            if not valid_seq:
                raise RuntimeError('Invalid gesture sequence. Malformed.')
            else:
                return gesture_name if not existing_seq else existing_seq

    def _create_gesture_name(self, sequence):
        """Create gesture name from sequence"""
        return 'gesture-' + ''.join(str(n) for n in sequence)

    def _validate_sequence(self, sensor_sequence):
        """Validate a requested gesture sequence"""
        new_seq = []
        valid = False
        lns = len(sensor_sequence)
        if lns < 2:
            return None

        for idx, seq in enumerate(sensor_sequence):
            valid = False
            try:
                iseq = int(seq, 2)
            except ValueError:
                break
            if iseq > 7 or iseq < 0:
                break
            if idx == 0 and iseq != 0:
                break
            valid = True
            new_seq.append(iseq)

        return new_seq if valid else None

    @qi.bind(returnType=qi.List(qi.String),
             paramsType=(qi.String, ),
             methodName='getSequence')
    def get_sequence(self, gesture_name):
        """Get the sequence associated with a gesture name

        :param gesture_name: Name of gesture you want the sequence of

        :returns: Sequence (as list of strings) if it exists, None otherwise
        """
        for gesture in self.all_gestures:
            if gesture_name == gesture.name:
                return ['{0:03b}'.format(s) for s in gesture.sequence]
        return None

    @qi.bind(paramsType=(qi.String, ), methodName='getGesture')
    def get_gesture_string(self, sequence):
        """Get the sequence associated with a gesture name

        :param sequence: Sequence you want the gesture name of

        :returns: Sequence (as list of strings) if it exists, None otherwise"""
        gs = sequence.split(',')
        return self.get_gesture_list(gs)

    @qi.bind(paramsType=(qi.List(qi.String), ), methodName='getGesture')
    def get_gesture_list(self, sequence):
        """Get the sequence associated with a gesture name

        :param sequence: Sequence you want the gesture name of

        :returns: Sequence (as list of strings) if it exists, None otherwise"""
        try:
            c_seq = [int(x, 2) for x in sequence]
        except (TypeError, ValueError):
            return None
        for gesture in self.all_gestures:
            if c_seq == gesture.sequence:
                return gesture.name
        return None

    @qi.bind(returnType=qi.Map(qi.String, qi.List(qi.String)),
             methodName='getGestures')
    def get_gestures(self):
        """Get all gestures that have been defined in the system

        :returns: Dictionary (Map<String, List<String>>) of all gestures"""
        return {
            gesture.name: ['{0:03b}'.format(s) for s in gesture.sequence]
            for gesture in self.all_gestures
        }

    @qi.bind(returnType=qi.Bool,
             paramsType=(qi.String, ),
             methodName='setSettleTime')
    def set_settle_time_string(self, settle_time):
        """Update length of settling time.

        :param settle_time: Desired settling time, in seconds (Default: 0.04s)

        :returns: True if settle time successfully updated, RuntimeError otherwise."""
        settle_time = float(settle_time)
        return self.set_settle_time(settle_time)

    @qi.bind(returnType=qi.Bool,
             paramsType=(qi.Float, ),
             methodName='setSettleTime')
    def set_settle_time(self, settle_time):
        """Update length of settling time.

        :param settle_time: Desired settling time, in seconds (Default: 0.04s)

        :returns: True if settle time successfully updated, RuntimeError otherwise."""
        if self.sensor_lock.acquire():
            self._cancel_futures()
            set = self._set_settle_time(settle_time)
            self._clean_up()
            self.sensor_lock.release()
            if not set:
                raise RuntimeError('Invalid time value.')
            return set

    def _set_settle_time(self, settle_time):
        try:
            self.e_sim = int(settle_time * self.s_to_micros)
            return True
        except ValueError:
            return False

    @qi.bind(returnType=qi.Bool,
             paramsType=(qi.String, ),
             methodName='setHoldTime')
    def set_hold_time_string(self, hold_time):
        """Set length of hold time.

        :param hold_time: Desired hold time, in seconds (Default: 0.8s)

        :returns: True if hold time successfully updated, RuntimeError otherwise."""
        hold_time = float(hold_time)
        return self.set_hold_time(hold_time)

    @qi.bind(returnType=qi.Bool,
             paramsType=(qi.Float, ),
             methodName='setHoldTime')
    def set_hold_time(self, hold_time):
        """Set length of hold time.

        :param hold_time: Desired hold time, in seconds (Default: 0.8s)

        :returns: True if hold time successfully updated, RuntimeError otherwise."""
        if self.sensor_lock.acquire():
            self._cancel_futures()
            set = self._set_hold_time(hold_time)
            self._clean_up()
            self.sensor_lock.release()
            if not set:
                raise RuntimeError('Invalid time value.')
            return set

    def _set_hold_time(self, hold_time):
        try:
            self.d_hold = int(hold_time * self.s_to_micros)
            return True
        except ValueError:
            return False

    @qi.bind(returnType=qi.Bool,
             paramsType=(qi.String, ),
             methodName='setSequenceTime')
    def set_sequence_time_string(self, sequence_time):
        """Set length of sequence time.

        :param sequence_time: Desired sequence time, in seconds (Default: 0.2s)

        :returns: True if sequence time successfully updated, RuntimeError otherwise."""
        sequence_time = float(sequence_time)
        return self.set_sequence_time(sequence_time)

    @qi.bind(returnType=qi.Bool,
             paramsType=(qi.Float, ),
             methodName='setSequenceTime')
    def set_sequence_time(self, sequence_time):
        """Update length of sequence time.

        :param sequence_time: Desired sequence time, in seconds (Default: 0.2s)

        :returns: True if sequence time successfully updated, RuntimeError otherwise."""
        if self.sensor_lock.acquire():
            self._cancel_futures()
            set = self._set_sequence_time(sequence_time)
            self._clean_up()
            self.sensor_lock.release()
            if not set:
                raise RuntimeError('Invalid time value.')
            return set

    def _set_sequence_time(self, sequence_time):
        try:
            self.e_seq = int(sequence_time * self.s_to_micros)
            return True
        except ValueError:
            return False