def if_up(self, nodeid='1'):
     if os.geteuid() == 0:
         self.tun_if = TunInterface(nodeid)
     else:
         print("Warning: superuser required to start tun interface.")
class WpanApi(SpinelCodec):
    """ Helper class to format wpan command packets """

    def __init__(self, stream, nodeid, use_hdlc=FEATURE_USE_HDLC):

        self.tun_if = None
        self.stream = stream
        self.nodeid = nodeid

        self.use_hdlc = use_hdlc
        if self.use_hdlc:
            self.hdlc = Hdlc(self.stream)

        # PARSER state
        self.rx_pkt = []
        self.callback = defaultdict(list)  # Map prop_id to list of callbacks.

        # Fire up threads
        self._reader_alive = True
        self.tid_filter = set()
        self.__queue_prop = defaultdict(Queue.Queue)  # Map tid to Queue.
        self.queue_register()
        self.__start_reader()

    def __del__(self):
        self._reader_alive = False

    def __start_reader(self):
        """Start reader thread"""
        self._reader_alive = True
        # start serial->console thread
        self.receiver_thread = threading.Thread(target=self.stream_rx)
        self.receiver_thread.setDaemon(True)
        self.receiver_thread.start()

    def transact(self, command_id, payload="", tid=SPINEL.HEADER_DEFAULT):
        pkt = self.encode_packet(command_id, payload, tid)
        if CONFIG.DEBUG_LOG_SERIAL:
            msg = "TX Pay: (%i) %s " % (len(pkt), util.hexify_bytes(pkt))
            logging.debug(msg)

        if self.use_hdlc:
            pkt = self.hdlc.encode(pkt)
        self.stream_tx(pkt)

    def parse_rx(self, pkt):
        if not pkt:
            return

        if CONFIG.DEBUG_LOG_SERIAL:
            msg = "RX Pay: (%i) %s " % (
                len(pkt), str(map(util.hexify_int, pkt)))
            logging.debug(msg)

        length = len(pkt) - 2
        if length < 0:
            return

        spkt = "".join(map(chr, pkt))

        tid = self.parse_C(spkt[:1])
        (cmd_id, cmd_length) = self.parse_i(spkt[1:])
        pay_start = cmd_length + 1
        payload = spkt[pay_start:]

        try:
            handler = SPINEL_COMMAND_DISPATCH[cmd_id]
            cmd_name = handler.__name__
            handler(self, payload, tid)

        except Exception as _ex:
            print(traceback.format_exc())
            cmd_name = "CB_Unknown"
            logging.info("\n%s (%i): ", cmd_name, cmd_id)

        if CONFIG.DEBUG_CMD_RESPONSE:
            logging.info("\n%s (%i): ", cmd_name, cmd_id)
            logging.info("===> %s", util.hexify_str(payload))

    def stream_tx(self, pkt):
        # Encapsulate lagging and Framer support in self.stream class.
        self.stream.write(pkt)

    def stream_rx(self):
        """ Recieve thread and parser. """
        while self._reader_alive:
            if self.use_hdlc:
                self.rx_pkt = self.hdlc.collect()
            else:
                # size=None: Assume stream will always deliver packets
                pkt = self.stream.read(None)
                self.rx_pkt = util.packed_to_array(pkt)

            self.parse_rx(self.rx_pkt)

    class PropertyItem(object):
        """ Queue item for NCP response to property commands. """

        def __init__(self, prop, value, tid):
            self.prop = prop
            self.value = value
            self.tid = tid

    def callback_register(self, prop, cb):
        self.callback[prop].append(cb)

    def queue_register(self, tid=SPINEL.HEADER_DEFAULT):
        self.tid_filter.add(tid)
        return self.__queue_prop[tid]

    def queue_wait_prepare(self, _prop_id, tid=SPINEL.HEADER_DEFAULT):
        self.queue_clear(tid)

    def queue_add(self, prop, value, tid):
        cb_list = self.callback[prop]

        # Asynchronous handlers can consume message and not add to queue.
        if len(cb_list) > 0:
            consumed = cb_list[0](prop, value, tid)
            if consumed: return

        if tid not in self.tid_filter:
            return
        item = self.PropertyItem(prop, value, tid)
        self.__queue_prop[tid].put_nowait(item)

    def queue_clear(self, tid):
        with self.__queue_prop[tid].mutex:
            self.__queue_prop[tid].queue.clear()

    def queue_wait_for_prop(self, _prop, tid=SPINEL.HEADER_DEFAULT, timeout=TIMEOUT_PROP):
        try:
            item = self.__queue_prop[tid].get(True, timeout)
            # self.__queue_prop[tid].task_done()
        except Queue.Empty:
            item = None

        return item

    def if_up(self, nodeid='1'):
        if os.geteuid() == 0:
            self.tun_if = TunInterface(nodeid)
        else:
            print("Warning: superuser required to start tun interface.")

    def if_down(self):
        if self.tun_if:
            self.tun_if.close()
        self.tun_if = None

    def ip_send(self, pkt):
        pay = self.encode_i(SPINEL.PROP_STREAM_NET)

        pkt_len = len(pkt)
        pay += pack("<H", pkt_len)          # Start with length of IPv6 packet

        pkt_len += 2                       # Increment to include length word
        pay += pack("%ds" % pkt_len, pkt)  # Append packet after length

        self.transact(SPINEL.CMD_PROP_VALUE_SET, pay)

    def cmd_send(self, command_id, payload="", tid=SPINEL.HEADER_DEFAULT):
        self.queue_wait_prepare(None, tid)
        self.transact(command_id, payload, tid)
        self.queue_wait_for_prop(None, tid)

    def prop_change_async(self, cmd, prop_id, value, py_format='B',
                          tid=SPINEL.HEADER_DEFAULT):
        pay = self.encode_i(prop_id)
        if py_format != None:
            pay += pack(py_format, value)
        self.transact(cmd, pay, tid)

    def prop_insert_async(self, prop_id, value, py_format='B',
                          tid=SPINEL.HEADER_DEFAULT):
        self.prop_change_async(SPINEL.CMD_PROP_VALUE_INSERT, prop_id,
                               value, py_format, tid)

    def prop_remove_async(self, prop_id, value, py_format='B',
                          tid=SPINEL.HEADER_DEFAULT):
        self.prop_change_async(SPINEL.CMD_PROP_VALUE_REMOVE, prop_id,
                               value, py_format, tid)

    def __prop_change_value(self, cmd, prop_id, value, py_format='B',
                            tid=SPINEL.HEADER_DEFAULT):
        """ Utility routine to change a property value over SPINEL. """
        self.queue_wait_prepare(prop_id, tid)

        pay = self.encode_i(prop_id)
        if py_format != None:
            pay += pack(py_format, value)
        self.transact(cmd, pay, tid)

        result = self.queue_wait_for_prop(prop_id, tid)
        if result:
            return result.value
        else:
            return None

    def prop_get_value(self, prop_id, tid=SPINEL.HEADER_DEFAULT):
        """ Blocking routine to get a property value over SPINEL. """
        if CONFIG.DEBUG_LOG_PROP:
            handler = SPINEL_PROP_DISPATCH[prop_id]
            prop_name = handler.__name__
            print("PROP_VALUE_GET [tid=%d]: %s" % (tid & 0xF, prop_name))
        return self.__prop_change_value(SPINEL.CMD_PROP_VALUE_GET, prop_id,
                                        None, None, tid)

    def prop_set_value(self, prop_id, value, py_format='B',
                       tid=SPINEL.HEADER_DEFAULT):
        """ Blocking routine to set a property value over SPINEL. """
        if CONFIG.DEBUG_LOG_PROP:
            handler = SPINEL_PROP_DISPATCH[prop_id]
            prop_name = handler.__name__
            print("PROP_VALUE_SET [tid=%d]: %s" % (tid & 0xF, prop_name))
        return self.__prop_change_value(SPINEL.CMD_PROP_VALUE_SET, prop_id,
                                        value, py_format, tid)

    def prop_insert_value(self, prop_id, value, py_format='B',
                          tid=SPINEL.HEADER_DEFAULT):
        """ Blocking routine to insert a property value over SPINEL. """
        if CONFIG.DEBUG_LOG_PROP:
            handler = SPINEL_PROP_DISPATCH[prop_id]
            prop_name = handler.__name__
            print("PROP_VALUE_INSERT [tid=%d]: %s" % (tid & 0xF, prop_name))
        return self.__prop_change_value(SPINEL.CMD_PROP_VALUE_INSERT, prop_id,
                                        value, py_format, tid)

    def prop_remove_value(self, prop_id, value, py_format='B',
                          tid=SPINEL.HEADER_DEFAULT):
        """ Blocking routine to remove a property value over SPINEL. """
        if CONFIG.DEBUG_LOG_PROP:
            handler = SPINEL_PROP_DISPATCH[prop_id]
            prop_name = handler.__name__
            print("PROP_VALUE_REMOVE [tid=%d]: %s" % (tid & 0xF, prop_name))
        return self.__prop_change_value(SPINEL.CMD_PROP_VALUE_REMOVE, prop_id,
                                        value, py_format, tid)

    def get_ipaddrs(self, tid=SPINEL.HEADER_DEFAULT):
        """
        Return current list of ip addresses for the device.
        """
        value = self.prop_get_value(SPINEL.PROP_IPV6_ADDRESS_TABLE, tid)
        # TODO: clean up table parsing to be less hard-coded magic.
        if value is None:
            return None
        size = 0x1B
        addrs = [value[i:i + size] for i in xrange(0, len(value), size)]
        ipaddrs = []
        for addr in addrs:
            addr = addr[2:18]
            ipaddrs.append(ipaddress.IPv6Address(addr))
        return ipaddrs