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