Example #1
0
    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()
Example #2
0
 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))
Example #3
0
 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))
Example #4
0
    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()
Example #5
0
    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()
Example #6
0
    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()
Example #7
0
    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()
Example #8
0
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
Example #9
0
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