def __init__(self, stream, nodeid, use_hdlc=FEATURE_USE_HDLC, timeout=TIMEOUT_PROP, vendor_module=None): self.stream = stream self.nodeid = nodeid self.timeout = timeout self.use_hdlc = use_hdlc if self.use_hdlc: self.hdlc = Hdlc(self.stream) if vendor_module: # Hook vendor properties try: codec = importlib.import_module(vendor_module + '.codec') SPINEL_PROP_DISPATCH.update(codec.VENDOR_SPINEL_PROP_DISPATCH) except ImportError: pass # 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 test_hdlc_encode(self): """ Unit test for Hdle.encode method. """ hdlc = Hdlc(None) for in_hex, out_hex in self.VECTOR.iteritems(): in_binary = binascii.unhexlify(in_hex) out_binary = hdlc.encode(in_binary) #print "inHex = "+binascii.hexlify(in_binary) #print "outHex = "+binascii.hexlify(out_binary) self.failUnless(out_hex == binascii.hexlify(out_binary))
def test_hdlc_encode(self): """ Unit test for Hdle.encode method. """ hdlc = Hdlc(None) for in_hex, out_hex in self.VECTOR.iteritems(): in_binary = binascii.unhexlify(in_hex) out_binary = hdlc.encode(in_binary) # print "inHex = "+binascii.hexlify(in_binary) # print "outHex = "+binascii.hexlify(out_binary) self.failUnless(out_hex == binascii.hexlify(out_binary))
def __init__(self, stream, nodeid, use_hdlc=FEATURE_USE_HDLC): 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 __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 = [] # Fire up threads self._reader_alive = True self.tid_filter = set() self.__queue_prop = defaultdict(Queue.Queue) self.queue_register() self.__start_reader()
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
class WpanApi(SpinelCodec): """ Helper class to format wpan command packets """ def __init__(self, stream, nodeid, use_hdlc=FEATURE_USE_HDLC, timeout=TIMEOUT_PROP, vendor_module=None): self.stream = stream self.nodeid = nodeid self.timeout = timeout self.use_hdlc = use_hdlc if self.use_hdlc: self.hdlc = Hdlc(self.stream) if vendor_module: # Hook vendor properties try: codec = importlib.import_module(vendor_module + '.codec') SPINEL_PROP_DISPATCH.update(codec.VENDOR_SPINEL_PROP_DISPATCH) except ImportError: pass # 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 __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): 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=bytes(), tid=SPINEL.HEADER_DEFAULT): pkt = self.encode_packet(command_id, payload, tid) if CONFIG.DEBUG_LOG_SERIAL: msg = "TX Pay: (%i) %s " % (len(pkt), binascii.hexlify(pkt).decode('utf-8')) CONFIG.LOGGER.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), binascii.hexlify(pkt).decode('utf-8')) CONFIG.LOGGER.debug(msg) length = len(pkt) - 2 if length < 0: return spkt = pkt #if not isinstance(spkt, str): # spkt = "".join(map(chr, spkt)) 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" CONFIG.LOGGER.info("\n%s (%i): ", cmd_name, cmd_id) if CONFIG.DEBUG_CMD_RESPONSE: CONFIG.LOGGER.info("\n%s (%i): ", cmd_name, cmd_id) CONFIG.LOGGER.info("===> %s", binascii.hexlify(payload).decode('utf-8')) 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. """ try: 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) except: if self._reader_alive: raise else: # Ignore the error since we are exiting pass 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_get(self, tid, timeout=None): try: if (timeout): item = self.__queue_prop[tid].get(True, timeout) else: item = self.__queue_prop[tid].get_nowait() except queue.Empty: item = None return item def queue_wait_for_prop(self, _prop, tid=SPINEL.HEADER_DEFAULT, timeout=None): if _prop is None: return None if timeout is None: timeout = self.timeout processed_queue = queue.Queue() timeout_time = time.time() + timeout while time.time() < timeout_time: item = self.queue_get(tid, timeout_time - time.time()) if item is None: continue if item.prop == _prop: break processed_queue.put_nowait(item) else: item = None # To make sure that all received properties will be processed in the same order. with self.__queue_prop[tid].mutex: while self.__queue_prop[tid]._qsize() > 0: processed_queue.put(self.__queue_prop[tid]._get()) while not processed_queue.empty(): self.__queue_prop[tid]._put(processed_queue.get_nowait()) return item 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 += pkt # Append packet after length self.transact(SPINEL.CMD_PROP_VALUE_SET, pay) def cmd_reset(self): self.queue_wait_prepare(None, SPINEL.HEADER_ASYNC) self.transact(SPINEL.CMD_RESET) result = self.queue_wait_for_prop(SPINEL.PROP_LAST_STATUS, SPINEL.HEADER_ASYNC) return (result is not None and result.value == 114) def cmd_send(self, command_id, payload=bytes(), 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 range(0, len(value), size)] ipaddrs = [] for addr in addrs: addr = addr[2:18] ipaddrs.append(ipaddress.IPv6Address(addr)) return ipaddrs