Пример #1
0
    def test_abort_on_responder(self, sim_engine):
        sim_engine = sim_engine(**COMMON_SIM_ENGINE_ARGS)

        install_sf(sim_engine.motes, SchedulingFunctionTwoStepForAbortion)
        root = sim_engine.motes[0]
        mote = sim_engine.motes[1]

        root.sf.issue_add_request(mote.get_mac_addr())
        u.run_until_asn(sim_engine, 101)

        # mote should receive the request
        logs = u.read_log_file([SimLog.LOG_SIXP_RX['type']])
        assert len(logs) == 1
        assert logs[0]['_mote_id'] == mote.id

        # we should have two sixp.tx logs: one is for the request, the other is
        # for response
        logs = u.read_log_file([SimLog.LOG_SIXP_TX['type']])
        assert len(logs) == 2
        assert logs[0]['_mote_id'] == root.id
        assert logs[1]['_mote_id'] == mote.id
        assert logs[1]['packet']['app']['msgType'] == d.SIXP_MSG_TYPE_RESPONSE

        # abort the transaction on the responder
        assert len(mote.sixp.transaction_table) == 1
        assert len(mote.tsch.txQueue) == 1

        # handler should receive the "aborted" event
        assert mote.sf.received_aborted_event is False
        mote.sixp.abort_transaction(initiator_mac_addr=root.get_mac_addr(),
                                    responder_mac_addr=mote.get_mac_addr())
        assert mote.sf.received_aborted_event is True

        assert len(mote.sixp.transaction_table) == 0
        assert len(mote.tsch.txQueue) == 0
Пример #2
0
    def test_abort_on_initiator(self, sim_engine, fixture_msg_type):
        sim_engine = sim_engine(**COMMON_SIM_ENGINE_ARGS)

        install_sf(sim_engine.motes, SchedulingFunctionThreeStep)
        root = sim_engine.motes[0]
        mote = sim_engine.motes[1]

        assert len(root.sixp.transaction_table) == 0
        assert len(root.tsch.txQueue) == 0

        root.sf.issue_add_request(mote.get_mac_addr())

        if fixture_msg_type == d.SIXP_MSG_TYPE_REQUEST:
            logs = u.read_log_file([SimLog.LOG_SIXP_TX['type']])
            assert len(logs) == 1
            assert logs[0]['_mote_id'] == root.id
            assert logs[0]['packet']['app']['msgType'] == (
                d.SIXP_MSG_TYPE_REQUEST
            )
        else:
            assert fixture_msg_type == d.SIXP_MSG_TYPE_CONFIRMATION
            u.run_until_asn(sim_engine, 102)

            # we should have two sixp.rx logs: one is for the request, the
            # other is for response
            logs = u.read_log_file([SimLog.LOG_SIXP_RX['type']])
            assert len(logs) == 2
            assert logs[0]['_mote_id'] == mote.id
            assert logs[1]['_mote_id'] == root.id

            # we should have three sixp.tx logs for each msg_type
            logs = u.read_log_file([SimLog.LOG_SIXP_TX['type']])
            assert len(logs) == 3
            assert logs[0]['_mote_id'] == root.id
            assert logs[1]['_mote_id'] == mote.id
            assert logs[2]['_mote_id'] == root.id
            assert logs[2]['packet']['app']['msgType'] == (
                d.SIXP_MSG_TYPE_CONFIRMATION
            )

        # abort the transaction on the initiator
        assert len(root.sixp.transaction_table) == 1
        assert len(root.tsch.txQueue) == 1

        root.sixp.abort_transaction(
            initiator_mac_addr = root.get_mac_addr(),
            responder_mac_addr = mote.get_mac_addr()
        )

        assert len(root.sixp.transaction_table) == 0
        assert len(root.tsch.txQueue) == 0
Пример #3
0
def test_app_upstream(
        sim_engine,
        app,
        fixture_dao_period
    ):

    # at least one app packet should be observed during the simulation

    sim_engine = sim_engine(
        {
            'exec_numMotes'                            : 2,
            'exec_numSlotframesPerRun'                 : 1000,
            'sf_class'                                 : 'SFNone',
            'conn_class'                               : 'Linear',
            'secjoin_enabled'                          : False,
            'app'                                      : app,
            'app_pkPeriod'                             : 2,
            'app_pkPeriodVar'                          : 0,
            'app_pkLength'                             : 90,
            'app_burstTimestamp'                       : 1,
            'app_burstNumPackets'                      : 5,
            'rpl_daoPeriod'                            : fixture_dao_period
        }
    )

    # give the network time to form
    u.run_until_end(sim_engine)

    # the number of 'app.tx' is the same as the number of generated packets.
    logs = u.read_log_file(filter=['app.tx'])

    # five packets should be generated per application
    assert len(logs) > 0
Пример #4
0
def test_app_upstream(sim_engine, app):
    """Test Application Upstream Traffic
    - objective   : test if app generates and sends packets as expected
    - precondition: form a 2-mote linear network
    - precondition: app sends 5 packets during the simulation time
    - action      : run the simulation for 10 seconds
    - expectation : each application sends five packets
    """

    sim_engine = sim_engine(
        {
            'exec_numMotes': 2,
            'exec_numSlotframesPerRun': 11,
            'sf_class': 'SFNone',
            'conn_class': 'Linear',
            'tsch_probBcast_ebProb': 0,
            'app': app,
            'app_pkPeriod': 2,
            'app_pkPeriodVar': 0,
            'app_pkLength': 90,
            'app_burstTimestamp': 1,
            'app_burstNumPackets': 5,
        },
        force_initial_routing_and_scheduling_state=True,
    )

    # give the network time to form
    u.run_until_asn(sim_engine, 1010)

    # the number of 'app.tx' is the same as the number of generated packets.
    logs = u.read_log_file(filter=['app.tx'])

    # five packets should be generated per application
    assert 5 <= len(logs)
Пример #5
0
def test_dodag_parent(sim_engine, fixture_rank_value):
    sim_engine = sim_engine(diff_config={
        'exec_numMotes': 3,
        'secjoin_enabled': False
    })

    root = sim_engine.motes[0]
    parent = sim_engine.motes[1]
    child = sim_engine.motes[2]

    # get them connected to the network
    eb = root.tsch._create_EB()
    parent.tsch._action_receiveEB(eb)
    child.tsch._action_receiveEB(eb)

    dio = root.rpl._create_DIO()
    dio['mac'] = {'srcMac': root.get_mac_addr()}
    parent.rpl.action_receiveDIO(dio)

    dio = parent.rpl._create_DIO()
    dio['mac'] = {'srcMac': parent.get_mac_addr()}
    child.rpl.action_receiveDIO(dio)

    # make sure they are ready for the test
    assert parent.clear_to_send_EBs_DATA() is True
    assert child.clear_to_send_EBs_DATA() is True
    assert len(child.rpl.of.parents) == 1
    assert child.rpl.of.parents[0]['mac_addr'] == parent.get_mac_addr()

    # create a DIO of 'parent' fot the test
    dio = parent.rpl._create_DIO()
    dio['mac'] = {'srcMac': parent.get_mac_addr()}
    if fixture_rank_value == 'smaller':
        dio['app']['rank'] = child.rpl.get_rank() - d.RPL_MINHOPRANKINCREASE
    elif fixture_rank_value == 'same':
        dio['app']['rank'] = child.rpl.get_rank()
    elif fixture_rank_value == 'larger':
        dio['app']['rank'] = child.rpl.get_rank() + d.RPL_MINHOPRANKINCREASE
    else:
        raise NotImplementedError()

    # process the global clock
    sim_engine.asn += 10

    # give the dio to 'child'
    child.rpl.action_receiveDIO(dio)

    # see what happened
    if fixture_rank_value == 'smaller':
        # the parent should remain in the parent set of the child
        assert child.rpl.of.parents[0]['mac_addr'] == parent.get_mac_addr()
    else:
        # the parent should have been removed from the parent set of the child
        assert len(child.rpl.of.parents) == 0
        # the child should send a DIO having INFINITE_RANK
        logs = u.read_log_file(filter=[SimLog.LOG_RPL_DIO_TX['type']],
                               after_asn=sim_engine.getAsn() - 1)
        assert len(logs) == 1
        assert logs[0]['packet']['app']['rank'] == 65535
Пример #6
0
def check_all_nodes_send_x(motes, x):
    senders = list(
        set([
            l['_mote_id']
            for l in u.read_log_file([SimLog.LOG_TSCH_TXDONE['type']])
            if l['packet']['type'] == x
        ]))
    assert sorted(senders) == sorted([m.id for m in motes])
Пример #7
0
def rpl_check_all_motes_send_DAOs(motes):
    senders = list(
        set([
            l['_mote_id']
            for l in u.read_log_file([SimLog.LOG_TSCH_TXDONE['type']])
            if l['packet']['type'] == 'DAO'
        ]))
    assert sorted(senders) == sorted([m.id for m in motes if m.id != 0])
Пример #8
0
    def test_parent_switch(self, sim_engine):
        sim_engine = sim_engine(
            diff_config={
                'exec_numSlotframesPerRun': 4000,
                'exec_numMotes': 3,
                'app_pkPeriod': 0,
                'sf_class': 'MSF',
                'conn_class': 'Linear'
            })

        # for quick access
        root = sim_engine.motes[0]
        mote_1 = sim_engine.motes[1]
        mote_2 = sim_engine.motes[2]
        asn_at_end_of_simulation = (
            sim_engine.settings.tsch_slotframeLength *
            sim_engine.settings.exec_numSlotframesPerRun)

        # wait for hop_2 to get ready. this is when the network is ready to
        # operate.
        u.run_until_mote_is_ready_for_app(sim_engine, mote_2)
        assert sim_engine.getAsn() < asn_at_end_of_simulation

        # stop DIO (and EB) transmission
        sim_engine.settings.tsch_probBcast_ebProb = 0

        # force mote_1 to switch its preferred parent
        old_parent = root
        new_parent = mote_2

        # invalidate old_parent
        dio = old_parent.rpl._create_DIO()
        dio['mac'] = {'srcMac': old_parent.get_mac_addr()}
        dio['app']['rank'] = 65535
        mote_1.rpl.action_receiveDIO(dio)
        # give a DIO from new_parent with a good rank
        dio = new_parent.rpl._create_DIO()
        dio['mac'] = {'srcMac': new_parent.get_mac_addr()}
        dio['app']['rank'] = 255
        mote_1.rpl.action_receiveDIO(dio)

        # mote_1 should issue CLEAR to the old preferred parent and ADD to the
        # new one
        asn_start_testing = sim_engine.getAsn()
        u.run_until_end(sim_engine)
        logs = u.read_log_file(filter=[SimLog.LOG_SIXP_TX['type']],
                               after_asn=asn_start_testing)

        def it_is_clear_request(packet):
            # return if the packet is a CLEAR request sent from mote_1 to
            # new_parent
            return ((packet['mac']['srcMac'] == mote_1.get_mac_addr())
                    and (packet['mac']['dstMac'] == old_parent.get_mac_addr())
                    and (packet['type'] == d.PKT_TYPE_SIXP)
                    and (packet['app']['msgType'] == d.SIXP_MSG_TYPE_REQUEST)
                    and (packet['app']['code'] == d.SIXP_CMD_CLEAR))

        assert len([l for l in logs if it_is_clear_request(l['packet'])]) > 0
Пример #9
0
    def test_no_fragment_loss(self, sim_engine, app_pkLength, fragmentation,
                              fragmentation_ff_discard_vrb_entry_policy):
        """ Test it with a basic case in which there is no fragment loss
        - objective   : test if packets are delivered to the root (destination)
        - precondition: form a 3-mote linear topology
        - action      : send packets from each motes except for the root
        - expectation : the root receives the packets
        """

        sim_engine = sim_engine(
            {
                'exec_numMotes':
                3,
                'exec_numSlotframesPerRun':
                10000,
                'sf_class':
                'SFNone',
                'conn_class':
                'Linear',
                'app_pkPeriod':
                5,
                'app_pkPeriodVar':
                0,
                'tsch_probBcast_ebProb':
                0,
                'sixlowpan_reassembly_buffers_num':
                2,
                'app_pkLength':
                app_pkLength,
                'fragmentation':
                fragmentation,
                'fragmentation_ff_discard_vrb_entry_policy':
                fragmentation_ff_discard_vrb_entry_policy
            },
            force_initial_routing_and_scheduling_state=True,
        )

        # run the simulation for 1000 timeslots (10 seconds)
        u.run_until_asn(sim_engine, 1000)

        # the root should receive packet from both of the two motes during 10 seconds.
        # - Packets are generated at every 5 seconds
        # - The first packet is generated within the first 5 seconds
        # - the minimum e2e latency of one fragment from the leaf is about 2 sec
        # - a packet is divided into two fragments at most in this test
        # - two fragments from the leaf need at least 4 sec to reach the root
        senders = []
        for log in u.read_log_file(filter=['app.rx']):
            srcIp = log['packet']['net']['srcIp']
            if srcIp not in senders:
                senders.append(srcIp)
            if len(senders) == 2:
                # root should receive packets from both of the two motes
                # if it reaches here, it means success
                return

        assert False
Пример #10
0
    def test_fragmentation_and_reassembly(
            self, sim_engine, app_pkLength, fragmentation,
            fragmentation_ff_discard_vrb_entry_policy):
        """Test fragmentation and reassembly themselves (w/o forwarding)
        - objective   : test if a packet is divided to the expected number
        - precondition: form a 2-mote linear topology
        - precondition: app scheduled is done by hand (app_pkPeriod=0)
        - action      : send a packet to the root
        - expectation : the number of fragments is the expected value
        """
        sim_engine = sim_engine(
            diff_config={
                'exec_numMotes':
                2,
                'exec_numSlotframesPerRun':
                20,
                'sf_class':
                'SFNone',
                'conn_class':
                'Linear',
                'app_pkPeriod':
                0,
                'app_pkPeriodVar':
                0,
                'tsch_probBcast_ebProb':
                0,
                'tsch_max_payload_len':
                self.TSCH_MAX_PAYLOAD,
                'tsch_tx_queue_size':
                self.TSCH_TX_QUEUE_SIZE,
                'app_pkLength':
                app_pkLength,
                'fragmentation':
                fragmentation,
                'fragmentation_ff_discard_vrb_entry_policy':
                fragmentation_ff_discard_vrb_entry_policy
            },
            force_initial_routing_and_scheduling_state=True)

        # send a packet from the leaf mote
        leaf = sim_engine.motes[1]
        # _send_a_single_packet() causes leaf to send a packet
        leaf.app._send_a_single_packet()

        # it's ready to test; run the simulation for long enough time
        u.run_until_asn(sim_engine, 1500)

        # check if fragment receptions happen the expected times
        logs = u.read_log_file(filter=['sixlowpan.pkt.rx'])
        assert (len([
            log for log in logs if log['packet']['type'] == d.PKT_TYPE_FRAG
        ]) == math.ceil(float(app_pkLength) / self.TSCH_MAX_PAYLOAD))
Пример #11
0
def test_network_advertisement(sim_engine, fixture_adv_frame):
    sim_engine = sim_engine(
        diff_config={
            'exec_numMotes': 1,
            'exec_numSlotframesPerRun':
            100,  # with 101 slots per slotframe, that's 10,100 slot total
        })

    u.run_until_asn(sim_engine, 10000)

    logs = u.read_log_file(filter=['tsch.txdone'])
    # root should send more than one EB in a default simulation run
    assert len([l
                for l in logs if l['packet']['type'] == fixture_adv_frame]) > 0
def test_compute_battery_lifetime(sim_engine):
    # reproduce Issue #360
    sim_engine = sim_engine(
        diff_config={
            'exec_numSlotframesPerRun': 1,
            'exec_numMotes': 2,
            'phy_numChans': 1,
            'radio_stats_log_period_s': 60
        })
    root = sim_engine.motes[0]
    mote = sim_engine.motes[1]

    # set 0% of PDR to their links
    channel = d.TSCH_HOPPING_SEQUENCE[0]
    sim_engine.connectivity.matrix.set_pdr_both_directions(mote_id_1=root.id,
                                                           mote_id_2=mote.id,
                                                           channel=channel,
                                                           pdr=0)

    # make up a radio activity of mote, which is supposed to consume
    # energy
    mote.radio.stats['tx_data'] = 100

    # force mote to log radio stats (at ASN 0)
    mote.radio._log_stats()

    # stop the simulator at the last ASN
    u.run_until_asn(sim_engine, 101)

    # make mote synched
    mote.tsch.setIsSync(True)

    # confirm we have relevant logs
    logs = u.read_log_file(['radio.stats', 'tsch.synced'])
    logs = [log for log in logs if log['_mote_id'] == mote.id]
    assert len(logs) == 2

    logs[0]['_type'] == 'radio.status'
    logs[0]['_asn'] == 0

    logs[1]['_type'] == 'tsch.synced'
    logs[1]['_asn'] == 101

    # run compute_kpis, which should end without raising an exception
    output = run_compute_kpis_py()

    # test done
    assert True
Пример #13
0
def test_dis_timer(sim_engine):
    sim_engine = sim_engine(
        diff_config={
            'exec_numMotes': 2,
            'rpl_extensions': ['dis_unicast'],
            'secjoin_enabled': False,
            'app_pkPeriod': 0,
            'tsch_keep_alive_interval': 0,
            'exec_numSlotframesPerRun': rpl.Rpl.DEFAULT_DIS_INTERVAL_SECONDS +
            1
        })

    root = sim_engine.motes[0]
    mote = sim_engine.motes[1]

    # get mote synchronized
    eb = root.tsch._create_EB()
    eb_dummy = {
        'type': d.PKT_TYPE_EB,
        'mac': {
            'srcMac': '00-00-00-AA-AA-AA',  # dummy
            'dstMac': d.BROADCAST_ADDRESS,  # broadcast
            'join_metric': 1000
        }
    }
    mote.tsch._action_receiveEB(eb)
    mote.tsch._action_receiveEB(eb_dummy)

    # disable root's communication capability by deleting the minimal shared
    # cell
    root.tsch.delete_minimal_cell()

    # run the simulation
    u.run_until_end(sim_engine)

    # collect DIS logs
    logs = u.read_log_file(filter=[SimLog.LOG_RPL_DIS_TX['type']])

    # check DIS packets
    assert len(logs) > 1
    # the first DIS should be sent to the link-local address of root because
    # 'dis_unicast' is specified
    logs[0]['packet']['net']['dstIp'] = root.get_ipv6_link_local_addr()
    # the rest should be sent to the multicast address despite of 'dis_unicast'
    for i in range(1, len(logs)):
        assert logs[i]['packet']['net'][
            'dstIp'] == d.IPV6_ALL_RPL_NODES_ADDRESS
Пример #14
0
def test_lockon(sim_engine):
    sim_engine = sim_engine(
        diff_config={
            'exec_numMotes': 2,
            'exec_numSlotframesPerRun': 1,
            'conn_class': 'Linear',
            'app_pkPeriod': 0,
            'secjoin_enabled': False,
            'sf_class': 'SFNone',
            'tsch_probBcast_ebProb': 0,
            'rpl_daoPeriod': 0
        })

    # short-hands
    root = sim_engine.motes[0]
    hop_1 = sim_engine.motes[1]

    # force hop_1 to join the network
    eb = root.tsch._create_EB()
    hop_1.tsch._action_receiveEB(eb)
    dio = root.rpl._create_DIO()
    dio['mac'] = {'srcMac': root.get_mac_addr()}
    hop_1.rpl.action_receiveDIO(dio)

    # let hop_1 send an application packet
    hop_1.app._send_a_single_packet()

    # force random.random() to return 1, which will cause any frame not to be
    # received by anyone
    _random = random.random

    def return_one(self):
        return float(1)

    random.random = types.MethodType(return_one, random)

    # run the simulation
    u.run_until_end(sim_engine)

    # put the original random() back to random
    random.random = _random

    # root shouldn't lock on the frame hop_1 sent since root is not expected to
    # receive even the preamble of the packet.
    logs = u.read_log_file([SimLog.LOG_PROP_DROP_LOCKON['type']])
    assert len(logs) == 0
Пример #15
0
def test_advertising_link(sim_engine):
    # EB should be sent only on links having ADVERTISING on
    sim_engine = sim_engine(
        diff_config={
            'exec_numMotes': 1,
            'sf_class': 'SFNone',
            'tsch_slotframeLength': 2,
            'tsch_probBcast_ebProb': 1.0,
        })
    root = sim_engine.motes[0]
    # disable DIO so that there will be no traffic except for EBs.
    root.rpl.trickle_timer.stop()

    slotframe = root.tsch.get_slotframe(0)

    # make sure we have one cell
    assert len(slotframe.get_busy_slots()) == 1
    cells = slotframe.get_cells_by_mac_addr(None)
    assert len(cells) == 1

    # the link-type of the minimal cell should be ADVERTISING
    minimal_cell = cells[0]
    assert minimal_cell.slot_offset == 0
    assert minimal_cell.channel_offset == 0
    assert (sorted(minimal_cell.options) == sorted(
        [d.CELLOPTION_RX, d.CELLOPTION_TX, d.CELLOPTION_SHARED]))
    assert minimal_cell.mac_addr == None
    assert minimal_cell.link_type == d.LINKTYPE_ADVERTISING

    # add one cell whose link-type is NORMAL at slotoffset 1
    normal_cell = tsch.Cell(slot_offset=1,
                            channel_offset=1,
                            options=minimal_cell.options,
                            mac_addr=None,
                            is_advertising=False)
    assert normal_cell.link_type == d.LINKTYPE_NORMAL
    slotframe.add(normal_cell)

    # run the simulation; we should have EBs only on the minimal cells
    u.run_until_end(sim_engine)

    tx_logs = u.read_log_file(filter=[SimLog.LOG_TSCH_TXDONE['type']])
    assert len(tx_logs) > 0
    for tx_log in tx_logs:
        assert tx_log['slot_offset'] != normal_cell.slot_offset
Пример #16
0
def test_retransmission_count(sim_engine):
    sim_engine = sim_engine(diff_config={
        'exec_numSlotframesPerRun': 10,
        'exec_numMotes': 2,
        'app_pkPeriod': 0,
        'rpl_daoPeriod': 0,
        'tsch_probBcast_ebProb': 0,
        'secjoin_enabled': False,
        'tsch_keep_alive_interval': 0,
        'conn_class': 'Linear'
    },
                            force_initial_routing_and_scheduling_state=True)

    # short-hands
    root = sim_engine.motes[0]
    hop1 = sim_engine.motes[1]
    connectivity_matrix = sim_engine.connectivity.connectivity_matrix

    # stop DIO timer
    root.rpl.trickle_timer.stop()
    hop1.rpl.trickle_timer.stop()

    # set 0% of PDR to the link between the two motes
    for channel in range(sim_engine.settings.phy_numChans):
        connectivity_matrix[root.id][hop1.id][channel]['pdr'] = 0
        connectivity_matrix[hop1.id][root.id][channel]['pdr'] = 0

    # make hop1 send an application packet
    hop1.app._send_a_single_packet()

    # run the simulation
    u.run_until_end(sim_engine)

    # check the log
    tx_logs = u.read_log_file([SimLog.LOG_TSCH_TXDONE['type']])

    # hop1 should send out the frame six times: 1 for the initial transmission
    # and 5 for retransmissions
    assert len(tx_logs) == 1 + d.TSCH_MAXTXRETRIES
    for tx_log in tx_logs:
        assert tx_log['packet']['type'] == d.PKT_TYPE_DATA
        assert hop1.is_my_ipv6_addr(tx_log['packet']['net']['srcIp'])
        assert tx_log['packet']['app']['appcounter'] == 0
Пример #17
0
    def test_no_txrx_cell_allocation_to_parent(self, sim_engine):
        sim_engine = sim_engine(diff_config={
            'exec_numMotes': 2,
            'sf_class': 'MSF',
            'conn_class': 'Linear',
        })

        u.run_until_end(sim_engine)
        logs = [
            log for log in u.read_log_file(
                filter=[SimLog.LOG_TSCH_ADD_CELL['type']])
            if ((log['_mote_id'] == sim_engine.motes[1].id) and (
                sorted(log['cellOptions']) == sorted(
                    [d.CELLOPTION_TX, d.CELLOPTION_RX, d.CELLOPTION_SHARED]))
                and (log['neighbor'] is not None))
        ]

        # mote_1 shouldn't schedule one TX/RX/SHARED cell to its parent
        # (mote_0)
        assert len(logs) == 0
Пример #18
0
    def test_initial_dedicated_cell_allocation_to_parent(self, sim_engine):
        sim_engine = sim_engine(
            diff_config={
                'exec_numMotes': 2,
                'sf_class': 'MSF',
                'conn_class': 'Linear',
                'app_pkPeriod': 0
            })

        u.run_until_end(sim_engine)
        logs = [
            log for log in u.read_log_file(
                filter=[SimLog.LOG_TSCH_ADD_CELL['type']])
            if ((log['_mote_id'] == sim_engine.motes[1].id) and (
                sorted(log['cellOptions']) == sorted([d.CELLOPTION_TX])) and (
                    log['neighbor'] is not None))
        ]

        # mote_1 should schedule one dedicated cell to its parent
        # (mote_0)
        assert len(logs) == 1
Пример #19
0
    def test_no_available_cell(self, sim_engine, function_under_test):
        sim_engine = sim_engine(
            diff_config={
                'exec_numSlotframesPerRun': 1000,
                'exec_numMotes': 2,
                'app_pkPeriod': 0,
                'sf_class': 'MSF',
                'conn_class': 'Linear'
            })

        # for quick access
        root = sim_engine.motes[0]
        hop_1 = sim_engine.motes[1]
        asn_at_end_of_simulation = (
            sim_engine.settings.tsch_slotframeLength *
            sim_engine.settings.exec_numSlotframesPerRun)

        # wait for hop_1 to get ready.
        u.run_until_mote_is_ready_for_app(sim_engine, hop_1)
        assert sim_engine.getAsn() < asn_at_end_of_simulation

        # fill up the hop_1's schedule
        channel_offset = 0
        cell_options = [d.CELLOPTION_TX]
        used_slots = hop_1.tsch.get_busy_slots(hop_1.sf.SLOTFRAME_HANDLE)
        for _slot in range(sim_engine.settings.tsch_slotframeLength):
            if _slot in used_slots:
                continue
            else:
                hop_1.tsch.addCell(slotOffset=_slot,
                                   channelOffset=channel_offset,
                                   neighbor=root.get_mac_addr(),
                                   cellOptions=cell_options,
                                   slotframe_handle=hop_1.sf.SLOTFRAME_HANDLE)
        assert (len(hop_1.tsch.get_busy_slots(hop_1.sf.SLOTFRAME_HANDLE)) ==
                sim_engine.settings.tsch_slotframeLength)

        # put dummy stats so that scheduling adaptation can be triggered
        hop_1.sf.num_cells_elapsed = 100
        hop_1.sf.num_cells_used = hop_1.sf.num_cells_elapsed

        # trigger scheduling adaptation
        if function_under_test == 'adapt_to_traffic':
            hop_1.sf._adapt_to_traffic(root.id)
        elif function_under_test == 'relocate':
            relocating_cell = filter(
                lambda cell: cell.options == [d.CELLOPTION_TX],
                hop_1.tsch.get_cells(root.get_mac_addr(),
                                     hop_1.sf.SLOTFRAME_HANDLE))[0]
            hop_1.sf._request_relocating_cells(neighbor=root.get_mac_addr(),
                                               cell_options=[d.CELLOPTION_TX],
                                               num_relocating_cells=1,
                                               cell_list=[relocating_cell])
        else:
            # not implemented
            assert False

        # make sure the log is written into the file
        SimEngine.SimLog.SimLog().flush()

        # MSF should output a "schedule-full" error in the log file
        logs = u.read_log_file(
            filter=[SimLog.LOG_MSF_ERROR_SCHEDULE_FULL['type']],
            after_asn=sim_engine.getAsn() - 1)
        assert len(logs) == 1
        assert logs[0]['_mote_id'] == hop_1.id
Пример #20
0
def test_tsch_clock(sim_engine, with_keep_alive):
    diff_config = {
        'exec_numMotes'           : 3,
        'app_pkPeriod'            : 0,
        'app_pkPeriodVar'         : 0,
        'tsch_probBcast_ebProb'   : 0,
        'rpl_daoPeriod'           : 0,
        'exec_numSlotframesPerRun': 100,
        'conn_class'              : 'Linear'
    }
    if with_keep_alive is True:
        diff_config['tsch_keep_alive_interval'] = 10
    else:
        diff_config['tsch_keep_alive_interval'] = 0
    diff_config['exec_randomSeed'] = 7263092949079992026
    sim_engine = sim_engine(
        diff_config                                = diff_config,
        force_initial_routing_and_scheduling_state = True
    )

    log_type_clock_diff = 'clock_diff'
    sim_log = SimLog.SimLog()

    # static values
    macTsRxWait = 0.00222 # 2,220 usec defined for 2.4 GHz by IEEE 802.15.4-2015

    # shorthands
    root             = sim_engine.motes[0]
    hop_1            = sim_engine.motes[1]
    hop_2            = sim_engine.motes[2]
    slot_duration    = sim_engine.settings.tsch_slotDuration
    slotframe_length = sim_engine.settings.tsch_slotframeLength
    max_drift        = sim_engine.settings.tsch_clock_max_drift_ppm
    clock_interval   = 1.0 / sim_engine.settings.tsch_clock_frequency
    guard_time       = (macTsRxWait / 2) - (2 * clock_interval)

    def _check_and_log_clock_drift():
        # without keep-alive, difference between the two clocks is
        # getting bigger and bigger. but, it should be within
        # -max_drift*2 and +max_drift*2 with offset in the range
        # between 0 and clock_interval

        diff_1 = hop_1.tsch.clock.get_drift() - root.tsch.clock.get_drift()
        diff_2 = hop_2.tsch.clock.get_drift() - hop_1.tsch.clock.get_drift()
        elapsed_time = sim_engine.getAsn() * slot_duration
        lower_bound_drift = (
            elapsed_time * (-1 * max_drift * 2) + 0
        )
        upper_bound_drift = (
            elapsed_time * (+1 * max_drift * 2) + clock_interval
        )
        assert lower_bound_drift < diff_1
        assert diff_1 < upper_bound_drift

        assert lower_bound_drift < diff_2
        assert diff_2 < upper_bound_drift

        if with_keep_alive:
            assert abs(diff_1) < guard_time
            assert abs(diff_2) < guard_time

        # custom log
        for mote_id, diff in zip([hop_1.id, hop_2.id], [diff_1, diff_2]):
            sim_log.log(
                {'type': log_type_clock_diff, 'keys': ['_mote_id', 'value']},
                {'_mote_id': mote_id, 'value': diff}
            )
        _schedule_clock_drift_checking_and_logging()

    def _schedule_clock_drift_checking_and_logging():
        sim_engine.scheduleAtAsn(
            asn            = sim_engine.getAsn() + (1.0 / slot_duration),
            cb             = _check_and_log_clock_drift,
            uniqueTag      = 'check_and_log_clock_drift',
            intraSlotOrder = d.INTRASLOTORDER_ADMINTASKS
        )

    _schedule_clock_drift_checking_and_logging()
    u.run_until_end(sim_engine)

    keep_alive_logs = [
        log for log in u.read_log_file([SimLog.LOG_TSCH_TXDONE['type']]) if (
            log['packet']['type'] == d.PKT_TYPE_KEEP_ALIVE
        )
    ]

    if with_keep_alive is True:
        assert len(keep_alive_logs) > 0
    else:
        assert len(keep_alive_logs) == 0
Пример #21
0
    def test_retry(self, sim_engine):
        sim_engine = sim_engine(
            diff_config={
                'exec_numMotes': 2,
                'sf_class': 'MSF',
                'conn_class': 'Linear',
                'secjoin_enabled': False,
                'app_pkPeriod': 0,
                'rpl_daoPeriod': 0,
                'rpl_extensions': [],
                'tsch_keep_alive_interval': 0,
                'tsch_probBcast_ebProb': 0
            })
        root = sim_engine.motes[0]
        mote = sim_engine.motes[1]

        # make root not to respond to a 6P request
        root.sf.recv_request = SchedulingFunctionSFNone(root).recv_request

        # get the mote joined
        eb = root.tsch._create_EB()
        eb_dummy = {
            'type': d.PKT_TYPE_EB,
            'mac': {
                'srcMac': '00-00-00-AA-AA-AA',  # dummy
                'dstMac': d.BROADCAST_ADDRESS,  # broadcast
                'join_metric': 1000
            }
        }
        mote.tsch._action_receiveEB(eb)
        mote.tsch._action_receiveEB(eb_dummy)

        assert mote.tsch.isSync

        dio = root.rpl._create_DIO()
        dio['mac'] = {
            'srcMac': root.get_mac_addr(),
            'dstMac': d.BROADCAST_ADDRESS
        }
        # need to put the DIO to 6LoWPAN layer so that mote can learn
        # root's MAC address and schedule the autonomous shared cell.
        mote.sixlowpan.recvPacket(dio)

        assert mote.rpl.dodagId is not None

        # stop DIO timer to make this test simple
        root.rpl.trickle_timer.stop()
        mote.rpl.trickle_timer.stop()

        u.run_until_end(sim_engine)

        # we should see three 6P timeout logs
        logs = u.read_log_file(
            filter=[SimLog.LOG_SIXP_TRANSACTION_TIMEOUT['type']])
        assert (len([log for log in logs if log['_mote_id'] == mote.id
                     ]) == SchedulingFunctionMSF.MAX_RETRY + 1)

        # mote should lose its parent
        logs = u.read_log_file(filter=[SimLog.LOG_RPL_CHURN['type']])
        assert len(logs) == 2
        assert logs[0]['preferredParent'] == root.get_mac_addr()
        assert logs[1]['preferredParent'] is None
Пример #22
0
def test_tx_cell_selection(sim_engine, packet_type, destination,
                           expected_cellOptions):

    # cell selection rules:
    #
    # - [CELLOPTION_TX] should be used for a unicast packet to a neighbor to whom a sender
    #   has a dedicated TX cell
    # - [CELLOPTION_TX,CELLOPTION_RX,CELLOPTION_SHARED] should be used otherwise
    #
    # With force_initial_routing_and_scheduling_state True, each mote has one
    # shared (TX/RX/SHARED) cell and one TX cell to its parent.

    sim_engine = sim_engine(diff_config={
        'exec_numMotes': 3,
        'sf_class': 'SFNone',
        'conn_class': 'Linear',
        'app_pkPeriod': 0,
        'app_pkPeriodVar': 0,
        'tsch_probBcast_ebProb': 0,
    },
                            force_initial_routing_and_scheduling_state=True)

    parent = sim_engine.motes[0]
    mote = sim_engine.motes[1]
    child = sim_engine.motes[2]

    packet = {
        'type': packet_type,
        'app': {
            'rank': mote.rpl.get_rank(),
        },
        'net': {
            'srcIp': mote.get_ipv6_link_local_addr()
        },
    }

    # With packet_type=d.PKT_TYPE_DATA, we'll test if the right cell is chosen
    # to send a fragment. Set 180 to packet_length so that the packet is
    # divided into two fragments.
    if packet_type == d.PKT_TYPE_DATA:
        packet['net']['packet_length'] = 180

    # set destination IPv6 address and and a corresponding neighbor entry
    if destination == 'broadcast':
        packet['net']['dstIp'] = d.IPV6_ALL_RPL_NODES_ADDRESS
    elif destination == 'parent':
        packet['net']['dstIp'] = parent.get_ipv6_link_local_addr()
        mote.sixlowpan.on_link_neighbor_list.append(parent.get_mac_addr())
    elif destination == 'child':
        packet['net']['dstIp'] = child.get_ipv6_link_local_addr()
        mote.sixlowpan.on_link_neighbor_list.append(child.get_mac_addr())

    # send a packet to the target destination
    mote.sixlowpan.sendPacket(packet)

    # wait for long enough for the packet to be sent
    u.run_until_asn(sim_engine, 1000)

    # see logs
    logs = []

    # as mentioned above, we'll see logs for fragment packets when
    # packet_type=d.PKT_TYPE_DATA
    if packet_type == d.PKT_TYPE_DATA:
        test_packet_type = d.PKT_TYPE_FRAG
    else:
        test_packet_type = packet_type

    for log in u.read_log_file(filter=['tsch.txdone']):
        if ((mote.is_my_mac_addr(log['packet']['mac']['srcMac']))
                and (log['packet']['type'] == test_packet_type)):
            logs.append(log)

    # transmission could be more than one due to retransmission
    assert (len(logs) > 0)

    for log in logs:
        slotframe = mote.tsch.slotframes[0]
        cell = slotframe.get_cells_at_asn(log['_asn'])[0]
        assert cell.options == expected_cellOptions
Пример #23
0
def test_avg_hops(sim_engine, fragmentation, app_pkLength, pkt_loss_mode):
    sim_engine = sim_engine(
        diff_config={
            'exec_numSlotframesPerRun': 40,
            'exec_numMotes': 10,
            'fragmentation': fragmentation,
            'app': 'AppPeriodic',
            'app_pkPeriod': 0,
            'app_pkLength': app_pkLength,
            'tsch_probBcast_ebProb': 0,
            'rpl_daoPeriod': 0,
            'conn_class': 'Linear'
        },
        force_initial_routing_and_scheduling_state=True,
    )

    # in this test, the leaf sends two application packets. when pkt_loss_mode
    # is 'with_pkt_loss', the second application packet will be lost at the
    # root.

    # shorthands
    root = sim_engine.motes[0]
    leaf = sim_engine.motes[-1]
    sim_settings = SimSettings.SimSettings()

    # make the app send an application packet
    leaf.app._send_a_single_packet()

    # wait for some time
    u.run_until_asn(sim_engine, 2020)

    # the root should receive the first application packet
    logs = u.read_log_file([SimLog.LOG_APP_RX['type']])
    assert len(logs) == 1
    assert logs[0]['_mote_id'] == root.id
    assert logs[0]['packet']['net']['srcIp'] == leaf.get_ipv6_global_addr()
    assert logs[0]['packet']['net']['dstIp'] == root.get_ipv6_global_addr()
    assert logs[0]['packet']['type'] == d.PKT_TYPE_DATA

    # make the root not receive at 6LoWPAN layer anything if pkt_loss_mode is
    # 'with_pkt_loss'
    if pkt_loss_mode == 'with_pkt_loss':
        assert root.dagRoot is True

        def recvPacket(self, packet):
            # do nothing; ignore the incoming packet
            pass

        root.sixlowpan.recvPacket = types.MethodType(recvPacket,
                                                     root.sixlowpan)
    elif pkt_loss_mode == 'without_pkt_loss':
        # do nothing
        pass
    else:
        raise NotImplemented()

    # make the app send another application packet
    leaf.app._send_a_single_packet()

    # run the simulator until it ends
    u.run_until_end(sim_engine)

    # confirm the leaf sent two application packets
    logs = u.read_log_file([SimLog.LOG_APP_TX['type']])
    assert len(logs) == 2
    for i in range(2):
        assert logs[i]['_mote_id'] == leaf.id
        assert logs[i]['packet']['net']['srcIp'] == leaf.get_ipv6_global_addr()
        assert logs[i]['packet']['net']['dstIp'] == root.get_ipv6_global_addr()
        assert logs[i]['packet']['type'] == d.PKT_TYPE_DATA

    # run compute_kpis.py against the log file
    compute_kpis_path = os.path.join(os.path.dirname(__file__), '../bin',
                                     'compute_kpis.py')
    output = subprocess.check_output('{0} \'{1}\''.format(
        'python', compute_kpis_path),
                                     shell=True).split('\n')

    # remove blank lines
    output = [line for line in output if not re.match(r'^\s*$', line)]

    # confirm if compute_kpis.py referred the right log file
    # the first line of output has the log directory name
    assert (re.search(os.path.basename(sim_settings.getOutputFile()),
                      output[0]) is not None)

    # convert the body of the output, which is a JSON string, to an object
    json_string = '\n'.join(output[1:-1])
    kpis = json.loads(json_string)

    # the avg_hops should be the same number as leaf.id since we use a linear
    # topology here.
    assert kpis['null'][str(leaf.id)]['avg_hops'] == leaf.id
Пример #24
0
def test_pending_bit(sim_engine, fixture_pending_bit_enabled):
    sim_engine = sim_engine(
        diff_config={
            'exec_numMotes': 2,
            'exec_numSlotframesPerRun': 3,
            'sf_class': 'SFNone',
            'secjoin_enabled': False,
            'app_pkPeriod': 0,
            'rpl_daoPeriod': 0,
            'tsch_keep_alive_interval': 0,
            'conn_class': 'Linear'
        })

    # short-hands
    root = sim_engine.motes[0]
    mote = sim_engine.motes[1]

    # get the mote joined the network
    eb = root.tsch._create_EB()
    mote.tsch._action_receiveEB(eb)
    dio = root.rpl._create_DIO()
    dio['mac'] = {'srcMac': root.get_mac_addr()}
    mote.rpl.action_receiveDIO(dio)

    # activate the pending bit feature if necessary
    if fixture_pending_bit_enabled:
        root.tsch.enable_pending_bit()
        mote.tsch.enable_pending_bit()

    # add a shared cell on slot_offset 1 and channel offset 1
    root.tsch.addCell(1, 1, None, [d.CELLOPTION_RX])
    mote.tsch.addCell(1, 1, root.get_mac_addr(),
                      [d.CELLOPTION_TX, d.CELLOPTION_SHARED])

    # put two DATA packets and one DIO between them to the TX queue of the mote
    mote.tsch.txQueue = []
    assert len(mote.tsch.txQueue) == 0
    mote.app._send_packet(dstIp=root.get_ipv6_global_addr(), packet_length=10)
    mote.rpl._send_DIO()
    mote.app._send_packet(dstIp=root.get_ipv6_global_addr(), packet_length=10)
    assert len(mote.tsch.txQueue) == 3

    u.run_until_end(sim_engine)

    # check logs
    logs = [
        log for log in u.read_log_file(filter=[SimLog.LOG_TSCH_TXDONE['type']])
        if log['packet']['type'] == d.PKT_TYPE_DATA
    ]
    assert len(logs) == 2

    if fixture_pending_bit_enabled:
        # the second DATA packet is sent on the same channel as the first one
        # by the pending bit feature
        assert logs[0]['slot_offset'] == 1
        assert logs[0]['channel_offset'] == 1
        assert logs[1]['slot_offset'] == None
        assert logs[1]['channel_offset'] == None
        assert logs[0]['channel'] == logs[1]['channel']
        assert logs[1]['_asn'] - logs[0]['_asn'] == 1
    else:
        # two DATA packets should be sent on the shared cell in different slot
        # frames
        assert logs[0]['slot_offset'] == 1
        assert logs[0]['channel_offset'] == 1
        assert logs[1]['slot_offset'] == 1
        assert logs[1]['channel_offset'] == 1
        assert logs[0]['channel'] != logs[1]['channel']
        assert ((logs[1]['_asn'] -
                 logs[0]['_asn']) == sim_engine.settings.tsch_slotframeLength)
Пример #25
0
def test_retransmission_backoff_algorithm(sim_engine, cell_type):
    sim_engine = sim_engine(
        diff_config={
            'exec_numSlotframesPerRun': 10000,
            'exec_numMotes': 2,
            'app_pkPeriod': 0,
            'secjoin_enabled': False,
            'tsch_keep_alive_interval': 0
        })
    sim_log = SimLog.SimLog()

    # filter logs to make this test faster; we need only SimLog.LOG_TSCH_TXDONE
    sim_log.set_log_filters([SimLog.LOG_TSCH_TXDONE['type']])

    # for quick access
    root = sim_engine.motes[0]
    hop_1 = sim_engine.motes[1]
    slotframe_length = sim_engine.settings.tsch_slotframeLength

    # increase TSCH_MAXTXRETRIES so that we can have enough retransmission
    # samples to validate
    d.TSCH_MAXTXRETRIES = 100

    #== test setup ==

    u.run_until_everyone_joined(sim_engine)

    # make hop_1 ready to send an application packet
    assert hop_1.rpl.dodagId is None
    dio = root.rpl._create_DIO()
    dio['mac'] = {'srcMac': root.get_mac_addr()}
    hop_1.rpl.action_receiveDIO(dio)
    assert hop_1.rpl.dodagId is not None

    # make root ignore all the incoming frame for this test
    def ignoreRx(self, packet, channel):
        self.waitingFor = None
        isACKed = False
        return isACKed

    root.tsch.rxDone = types.MethodType(ignoreRx, root.tsch)

    if cell_type == 'dedicated-cell':
        # allocate one TX=1/RX=1/SHARED=1 cell to the motes as their dedicate cell.
        cellOptions = [d.CELLOPTION_TX, d.CELLOPTION_RX, d.CELLOPTION_SHARED]

        assert len(root.tsch.get_cells(hop_1.get_mac_addr())) == 0
        root.tsch.addCell(1, 1, hop_1.get_mac_addr(), cellOptions)
        assert len(root.tsch.get_cells(hop_1.get_mac_addr())) == 1

        assert len(hop_1.tsch.get_cells(root.get_mac_addr())) == 0
        hop_1.tsch.addCell(1, 1, root.get_mac_addr(), cellOptions)
        assert len(hop_1.tsch.get_cells(root.get_mac_addr())) == 1

    # make sure hop_1 send a application packet when the simulator starts
    hop_1.tsch.txQueue = []
    hop_1.app._send_a_single_packet()
    assert len(hop_1.tsch.txQueue) == 1

    #== start test ==
    asn_starting_test = sim_engine.getAsn()

    # run the simulator until hop_1 drops the packet or the simulation ends
    def drop_packet(self, packet, reason):
        if packet['type'] == d.PKT_TYPE_DATA:
            # pause the simulator
            sim_engine.pauseAtAsn(sim_engine.getAsn() + 1)

    hop_1.drop_packet = types.MethodType(drop_packet, hop_1)
    u.run_until_end(sim_engine)

    # confirm
    # - hop_1 sent the application packet to the root
    # - retransmission backoff worked
    logs = u.read_log_file(filter=[SimLog.LOG_TSCH_TXDONE['type']],
                           after_asn=asn_starting_test)
    app_data_tx_logs = []
    for log in logs:
        if ((log['_mote_id'] == hop_1.id)
                and (root.is_my_mac_addr(log['packet']['mac']['dstMac']))
                and (log['packet']['type'] == d.PKT_TYPE_DATA)):
            app_data_tx_logs.append(log)

    assert len(app_data_tx_logs) == 1 + d.TSCH_MAXTXRETRIES

    # all transmission should have happened only on the dedicated cell if it's
    # available (it shouldn't transmit a unicast frame to the root on the
    # minimal (shared) cell.
    if cell_type == 'dedicated-cell':
        _cell = hop_1.tsch.get_cells(root.get_mac_addr())[0]
        expected_cell_offset = _cell.slot_offset
    elif cell_type == 'shared-cell':
        expected_cell_offset = 0  # the minimal (shared) cell
    else:
        raise NotImplementedError()

    for log in app_data_tx_logs:
        slot_offset = log['_asn'] % slotframe_length
        assert slot_offset == expected_cell_offset

    # retransmission should be performed after backoff wait; we should see gaps
    # between consecutive retransmissions. If all the gaps are 101 slots, that
    # is, one slotframe, this means there was no backoff wait between
    # transmissions.
    timestamps = [log['_asn'] for log in app_data_tx_logs]
    diffs = map(lambda x: x[1] - x[0], zip(timestamps[:-1], timestamps[1:]))
    assert len([diff for diff in diffs if diff != slotframe_length]) > 0
Пример #26
0
def test_propagation(sim_engine, fixture_propagation_test_type):
    PERFECT_PDR = 1.0
    RSSI_VALUES = {
        'perfect_rssi': -10,
        'poor_rssi': -90,
        'worst_rssi': -97,  # the worst in rssi_pdr_table of Connectivity.py
        'invalid_rssi': -1000
    }

    num_motes = 2
    num_frames = 1000
    sim_engine = sim_engine(
        diff_config={
            'exec_numSlotframesPerRun': num_frames * (d.TSCH_MAXTXRETRIES + 1),
            'exec_numMotes': num_motes,
            'secjoin_enabled': False,
            'app_pkPeriod': 0,
            'rpl_of': 'OFNone',
            'rpl_daoPeriod': 0,
            'rpl_extensions': [],
            'sf_class': 'SFNone',
            'tsch_slotframeLength': 2,
            'tsch_probBcast_ebProb': 0,
            'tsch_keep_alive_interval': 0,
            'tsch_tx_queue_size': num_frames,
            'conn_class': 'Linear',  # this is intentional
            'phy_numChans': 1
        })
    root = sim_engine.motes[0]
    mote = sim_engine.motes[1]
    # aliases
    dst = root
    src = mote

    class TestConnectivityMatrixK7(ConnectivityMatrixK7):
        def _additional_initialization(self):
            # set up the connectivity matrix
            channel = d.TSCH_HOPPING_SEQUENCE[0]
            self.set_pdr(src.id, dst.id, channel, PERFECT_PDR)
            self.set_rssi(src.id, dst.id, channel,
                          RSSI_VALUES[fixture_propagation_test_type])
            # dump the connectivity matrix
            print 'The Connectivity Matrix ("1.0" means PDR of 100%):'
            self.dump()

    # replace the 'Linear' conn_class with the test purpose
    # conn_class, TestConnectivityMatrixK7
    if fixture_propagation_test_type != 'test_setup':
        sim_engine.connectivity.matrix = TestConnectivityMatrixK7(
            sim_engine.connectivity)

    # add a dedicated TX cell in order to avoid backoff wait
    slot_offset = 1
    channel_offset = 0
    dst.tsch.addCell(slot_offset, channel_offset, src.get_mac_addr(),
                     [d.CELLOPTION_RX])
    src.tsch.addCell(slot_offset, channel_offset, dst.get_mac_addr(),
                     [d.CELLOPTION_TX])

    # get mote synchronized
    eb = root.tsch._create_EB()
    mote.tsch._action_receiveEB(eb)

    # put a fake EB to mote so that it can get synchronized
    # immediately
    eb_dummy = {
        'type': d.PKT_TYPE_EB,
        'mac': {
            'srcMac': '00-00-00-AA-AA-AA',  # dummy
            'dstMac': d.BROADCAST_ADDRESS,  # broadcast
            'join_metric': 1000
        }
    }
    mote.tsch._action_receiveEB(eb_dummy)

    # disabled the trickle timer
    root.rpl.trickle_timer.stop()
    mote.rpl.trickle_timer.stop()

    # [test types]
    #
    # test_setup: verify if we can set up a test environment
    # correctly, where there is no background traffic
    #
    # perfect_rssi/poor_rssi: all the transmission should succeed
    # since the links between the two motes have a PDR of 100%
    # regardless of their RSSI values

    if fixture_propagation_test_type != 'test_setup':
        # put frames to the TX queue of the source; use the keep-alive
        # frame as the test packet
        for seqno in range(num_frames):
            packet = {
                'type': d.PKT_TYPE_KEEP_ALIVE,
                'mac': {
                    'srcMac': src.get_mac_addr(),
                    'dstMac': dst.get_mac_addr()
                },
                'app': {
                    'seq': seqno
                }  # for debugging purpose
            }
            src.tsch.enqueue(packet)

    u.run_until_end(sim_engine)

    num_transmissions = len(u.read_log_file([SimLog.LOG_TSCH_TXDONE['type']]))

    if fixture_propagation_test_type == 'test_setup':
        # we shouldn't see any transmission
        assert num_transmissions == 0
    else:
        # num_transmissions contains the number of retransmissions if
        # any. in other words, num_transmissions should be equal to
        # num_frames when no frame is dropped
        assert num_transmissions == num_frames
Пример #27
0
    def test_issue_188(self, sim_engine):
        """This test reproduces Issue #188

        An exception was raised when a mote receives a request which has
        a different SeqNum from one in a valid transaction it still
        has.
        """
        sim_engine = sim_engine(
            diff_config={
                'exec_numSlotframesPerRun': 10,
                'exec_numMotes': 2,
                'sf_class': 'SFNone',
                'conn_class': 'Linear',
                'tsch_probBcast_ebProb': 0
            },
            force_initial_routing_and_scheduling_state=True)

        # for quick access
        root = sim_engine.motes[0]
        hop_1 = sim_engine.motes[1]

        # step-0: set 1 (a non-zero value) to local SeqNum both of root and
        # hop_1
        root.sixp._get_seqnum(hop_1.get_mac_addr())  # create a SeqNum entry
        root.sixp.increment_seqnum(
            hop_1.get_mac_addr())  # increment the SeqNum
        hop_1.sixp._get_seqnum(root.get_mac_addr())  # create a SeqNum entry
        hop_1.sixp.increment_seqnum(
            root.get_mac_addr())  # increment the SeqNum

        # step-1: let hop_1 issue an ADD request. In order to make the
        # transaction expire on hop_1, set a shorter timeout value on hop_1's
        # side
        timeout_seconds = 0.01
        hop_1.sixp.send_request(dstMac=root.get_mac_addr(),
                                command=d.SIXP_CMD_ADD,
                                cellList=[],
                                timeout_seconds=timeout_seconds)

        # step-2: let root issue an CLEAR request when it receives an ADD
        # request from hop_1. Then, the root will have two transactions to
        # hop_1 with each direction.
        result = {'root_received_add_request': False}

        def recv_add_request(self, packet):
            assert packet['type'] == d.PKT_TYPE_SIXP
            assert packet['app']['msgType'] == d.SIXP_MSG_TYPE_REQUEST
            assert packet['app']['code'] == d.SIXP_CMD_ADD

            if result['root_received_add_request'] is False:
                result['root_received_add_request'] = True

                # send a CLAER request to the responder
                self.mote.sixp.send_request(dstMac=hop_1.get_mac_addr(),
                                            command=d.SIXP_CMD_CLEAR)
            else:
                # do nothing
                pass

        root.sf.recv_request = types.MethodType(recv_add_request, root.sf)

        # step-3: let hop_1 issue another ADD request when it receives a CLAER
        # request from the hop_1. Then, the root will have two transactions to
        # the responder for each direction.
        result['hop_1_received_clear_request'] = False

        def recv_clear_request(self, packet):
            assert packet['type'] == d.PKT_TYPE_SIXP
            assert packet['app']['msgType'] == d.SIXP_MSG_TYPE_REQUEST
            assert packet['app']['code'] == d.SIXP_CMD_CLEAR

            result['hop_1_received_clear_request'] = True

            # send a new ADD request, which causes an exception unless the
            # bugfix is in place.
            self.mote.sixp.send_request(dstMac=root.get_mac_addr(),
                                        command=d.SIXP_CMD_ADD,
                                        cellList=[])

        hop_1.sf.recv_request = types.MethodType(recv_clear_request, hop_1.sf)

        # run the simulator until the end; if there is no exception, it should
        # run through.
        u.run_until_end(sim_engine)

        assert result['root_received_add_request'] == True
        assert result['hop_1_received_clear_request'] == True

        # There should be one RC_ERR_BUSY from root
        logs = u.read_log_file([SimLog.LOG_SIXP_TX['type']])
        rc_err_busy_logs = [
            l for l in logs
            if ((l['packet']['app']['msgType'] == d.SIXP_MSG_TYPE_RESPONSE) and
                (l['packet']['app']['code'] == d.SIXP_RC_ERR_BUSY) and (
                    root.is_my_mac_addr(l['packet']['mac']['srcMac'])) and (
                        hop_1.is_my_mac_addr(l['packet']['mac']['dstMac'])))
        ]
        assert len(rc_err_busy_logs) == 1

        # RC_ERR_BUSY should be sent to the ADD request with SeqNum of 0
        assert rc_err_busy_logs[0]['packet']['app']['seqNum'] == 0
Пример #28
0
    def test_locked_slot_in_relocation_request(self, sim_engine):
        # MSF shouldn't select a slot offset out of the candidate cell
        # list which is in locked_slots
        sim_engine = sim_engine(
            diff_config={
                'exec_numMotes': 2,
                'sf_class': 'MSF',
                'conn_class': 'Linear',
                'secjoin_enabled': False,
                'app_pkPeriod': 0,
                'rpl_daoPeriod': 0,
                'rpl_extensions': [],
                'tsch_keep_alive_interval': 0,
                'tsch_probBcast_ebProb': 0
            })

        root = sim_engine.motes[0]
        mote = sim_engine.motes[1]

        u.get_join(root, mote)
        # wait for a while
        u.run_until_asn(sim_engine,
                        2 * sim_engine.settings.tsch_slotframeLength)

        # mote should have one dedicated cell
        cells = mote.tsch.get_cells(root.get_mac_addr(),
                                    mote.sf.SLOTFRAME_HANDLE)
        assert len(cells) == 2
        cells = [cell for cell in cells if cell.options == [d.CELLOPTION_TX]]
        assert len(cells) == 1
        cell = cells[0]

        # send a RELOCATE request to mote, which has the used slot
        # offset in both of the candidate cell list and the relocation
        # cell list
        target_slot_offset = cell.slot_offset + 1
        if (cell.slot_offset + 1) == sim_engine.settings.tsch_slotframeLength:
            target_slot_offset = 1
        # put target_slot_offset into locked_slots. target_slot_offset
        # is in the candidate cell list
        mote.sf.locked_slots.add(target_slot_offset)
        root.sixp.send_request(dstMac=mote.get_mac_addr(),
                               command=d.SIXP_CMD_RELOCATE,
                               cellOptions=[d.CELLOPTION_RX],
                               numCells=1,
                               relocationCellList=[{
                                   'slotOffset':
                                   cell.slot_offset,
                                   'channelOffset':
                                   cell.channel_offset
                               }],
                               candidateCellList=[{
                                   'slotOffset': target_slot_offset,
                                   'channelOffset': 0
                               }],
                               callback=None)

        u.run_until_asn(
            sim_engine,
            sim_engine.getAsn() + 2 * sim_engine.settings.tsch_slotframeLength)
        logs = u.read_log_file(filter=[SimLog.LOG_SIXP_RX['type']])
        assert len(logs) == 4  # including the first round-trip for ADD
        response = logs[-1]['packet']
        assert response['app']['msgType'] == d.SIXP_MSG_TYPE_RESPONSE
        assert response['app']['code'] == d.SIXP_RC_SUCCESS
        assert len(response['app']['cellList']) == 0
Пример #29
0
def test_tx_with_two_slotframes(sim_engine):
    sim_engine = sim_engine(
        diff_config={
            'app_pkPeriod': 0,
            'exec_numMotes': 2,
            'exec_numSlotframesPerRun': 1000,
            'secjoin_enabled': False,
            'sf_class': 'SFNone',
            'conn_class': 'Linear',
            'rpl_extensions': [],
            'rpl_daoPeriod': 0
        })

    # shorthands
    root = sim_engine.motes[0]
    hop_1 = sim_engine.motes[1]

    # add one slotframe to the two motes
    for mote in sim_engine.motes:
        mote.tsch.add_slotframe(1, 101)

    asn_at_end_of_simulation = (sim_engine.settings.tsch_slotframeLength *
                                sim_engine.settings.exec_numSlotframesPerRun)

    u.run_until_everyone_joined(sim_engine)
    assert sim_engine.getAsn() < asn_at_end_of_simulation

    # put DIO to hop1
    dio = root.rpl._create_DIO()
    dio['mac'] = {'srcMac': root.get_mac_addr()}
    hop_1.rpl.action_receiveDIO(dio)

    # install one TX cells to each slotframe
    for i in range(2):
        hop_1.tsch.addCell(slotOffset=i + 1,
                           channelOffset=0,
                           neighbor=root.get_mac_addr(),
                           cellOptions=[d.CELLOPTION_TX],
                           slotframe_handle=i)
        root.tsch.addCell(slotOffset=i + 1,
                          channelOffset=0,
                          neighbor=hop_1.get_mac_addr(),
                          cellOptions=[d.CELLOPTION_RX],
                          slotframe_handle=i)

    # the first dedicated cell is scheduled at slot_offset 1, the other is at
    # slot_offset 2
    cell_in_slotframe_0 = hop_1.tsch.get_cells(root.get_mac_addr(), 0)[0]
    cell_in_slotframe_1 = hop_1.tsch.get_cells(root.get_mac_addr(), 1)[0]

    # run until the end of this slotframe
    slot_offset = sim_engine.getAsn() % 101
    u.run_until_asn(sim_engine, sim_engine.getAsn() + (101 - slot_offset - 1))

    # send two application packets, which will be sent over the dedicated cells
    hop_1.app._send_a_single_packet()
    hop_1.app._send_a_single_packet()

    # run for one slotframe
    asn = sim_engine.getAsn()
    assert (asn % 101) == 100  # the next slot is slotoffset 0
    u.run_until_asn(sim_engine, asn + 101)

    # check logs
    ## TX side (hop_1)
    logs = [
        log for log in u.read_log_file(filter=[SimLog.LOG_TSCH_TXDONE['type']],
                                       after_asn=asn)
        if log['_mote_id'] == hop_1.id
    ]
    assert len(logs) == 2
    assert (logs[0]['_asn'] % 101) == cell_in_slotframe_0.slot_offset
    assert (logs[1]['_asn'] % 101) == cell_in_slotframe_1.slot_offset

    ## RX side (root)
    logs = [
        log for log in u.read_log_file(filter=[SimLog.LOG_TSCH_RXDONE['type']],
                                       after_asn=asn)
        if log['_mote_id'] == root.id
    ]
    assert len(logs) == 2
    assert (logs[0]['_asn'] % 101) == cell_in_slotframe_0.slot_offset
    assert (logs[1]['_asn'] % 101) == cell_in_slotframe_1.slot_offset

    # confirm hop_1 has the minimal cell
    assert len(hop_1.tsch.get_cells(None)) == 1
    assert (hop_1.tsch.get_cells(None)[0].options == [
        d.CELLOPTION_TX, d.CELLOPTION_RX, d.CELLOPTION_SHARED
    ])
Пример #30
0
    def test_msf(self, sim_engine):
        """ Test Scheduling Function Traffic Adaptation
        - objective   : test if msf adjust the number of allocated cells in
                        accordance with traffic
        - precondition: form a 2-mote linear network
        - precondition: the network is formed
        - action      : change traffic
        - expectation : MSF should trigger ADD/DELETE/RELOCATE accordingly
        """

        # to make this test easy, change
        # MSF_HOUSEKEEPINGCOLLISION_PERIOD to 1 second
        msf_housekeeping_period_backup = d.MSF_HOUSEKEEPINGCOLLISION_PERIOD
        d.MSF_HOUSEKEEPINGCOLLISION_PERIOD = 1

        sim_engine = sim_engine(
            diff_config={
                'exec_randomSeed': 3413860673863013345,
                'app_pkPeriod': 0,
                'app_pkPeriodVar': 0,
                'exec_numMotes': 2,
                'exec_numSlotframesPerRun': 4000,
                'rpl_daoPeriod': 0,
                'tsch_keep_alive_interval': 0,
                'tsch_probBcast_ebProb': 0,
                'secjoin_enabled': False,
                'sf_class': 'MSF',
                'conn_class': 'Linear',
            })

        # for quick access
        root = sim_engine.motes[0]
        mote = sim_engine.motes[1]

        # disable DIO
        def do_nothing(self):
            pass

        mote.rpl._send_DIO = types.MethodType(do_nothing, mote)

        # get the mote joined
        eb = root.tsch._create_EB()
        eb_dummy = {
            'type': d.PKT_TYPE_EB,
            'mac': {
                'srcMac': '00-00-00-AA-AA-AA',  # dummy
                'dstMac': d.BROADCAST_ADDRESS,  # broadcast
                'join_metric': 1000
            }
        }
        mote.tsch._action_receiveEB(eb)
        mote.tsch._action_receiveEB(eb_dummy)
        dio = root.rpl._create_DIO()
        dio['mac'] = {
            'srcMac': root.get_mac_addr(),
            'dstMac': d.BROADCAST_ADDRESS
        }
        mote.sixlowpan.recvPacket(dio)

        # 1. test autonomous cell installation
        # 1.1 test Non-SHARED autonomous cell
        cells = [
            cell for cell in mote.tsch.get_cells(
                mac_addr=None,
                slotframe_handle=SchedulingFunctionMSF.SLOTFRAME_HANDLE)
            if cell.options == [d.CELLOPTION_TX, d.CELLOPTION_RX]
        ]
        assert len(cells) == 1
        # 1.2 test SHARED autonomous cell to root
        cells = [
            cell for cell in mote.tsch.get_cells(
                mac_addr=root.get_mac_addr(),
                slotframe_handle=SchedulingFunctionMSF.SLOTFRAME_HANDLE)
            if cell.options ==
            [d.CELLOPTION_TX, d.CELLOPTION_RX, d.CELLOPTION_SHARED]
        ]
        assert len(cells) == 1

        # 2. test dedicated cell allocation
        # 2.1 decrease MSF_MIN_NUM_TX and MSF_MAX_NUMCELLS to speed up this test
        d.MSF_MIN_NUM_TX = 10
        d.MSF_MAX_NUMCELLS = 10

        # 2.2 confirm the mote doesn't have any dedicated cell to its parent
        cells = [
            cell for cell in mote.tsch.get_cells(
                mac_addr=root.get_mac_addr(),
                slotframe_handle=SchedulingFunctionMSF.SLOTFRAME_HANDLE)
            if cell.options == [d.CELLOPTION_TX]
        ]
        assert len(cells) == 0

        # 2.3 the mote should have triggered a 6P to allocate one
        # dedicated cell
        logs = u.read_log_file(filter=[SimLog.LOG_SIXP_TX['type']])
        assert len(logs) == 1
        packet = logs[0]['packet']
        assert packet['mac']['dstMac'] == root.get_mac_addr()
        assert packet['app']['msgType'] == d.SIXP_MSG_TYPE_REQUEST
        assert packet['app']['code'] == d.SIXP_CMD_ADD
        assert packet['app']['numCells'] == 1
        assert packet['app']['cellOptions'] == [d.CELLOPTION_TX]

        # in order to test the traffic adaptation mechanism of MSF,
        # disable the pending bit feature
        assert mote.tsch.pending_bit_enabled is True
        mote.tsch.pending_bit_enabled = False

        # wait until the managed cell is available
        u.run_until_asn(
            sim_engine,
            sim_engine.getAsn() + mote.settings.tsch_slotframeLength * 2)

        # mote should have one managed cell scheduled
        cells = [
            cell for cell in mote.tsch.get_cells(
                mac_addr=root.get_mac_addr(),
                slotframe_handle=SchedulingFunctionMSF.SLOTFRAME_HANDLE)
            if cell.options == [d.CELLOPTION_TX]
        ]
        assert len(cells) == 1

        # 2.4 send an application packet per slotframe
        mote.settings.app_pkPeriod = (mote.settings.tsch_slotframeLength / 2 *
                                      mote.settings.tsch_slotDuration)
        mote.app.startSendingData()

        # 2.5 run for 10 slotframes
        assert mote.sf.cell_utilization == 0.0
        u.run_until_asn(
            sim_engine,
            sim_engine.getAsn() + mote.settings.tsch_slotframeLength * 10)

        # 2.6 confirm the cell usage reaches 100%
        assert mote.sf.cell_utilization == 1.0

        # 2.7 one dedicated cell should be allocated in the next 2 slotframes
        u.run_until_asn(
            sim_engine,
            sim_engine.getAsn() + mote.settings.tsch_slotframeLength * 2)
        cells = [
            cell for cell in mote.tsch.get_cells(
                mac_addr=root.get_mac_addr(),
                slotframe_handle=SchedulingFunctionMSF.SLOTFRAME_HANDLE)
            if cell.options == [d.CELLOPTION_TX]
        ]
        assert len(cells) == 2
        slot_offset = cells[0].slot_offset

        # adjust the packet interval
        mote.settings.app_pkPeriod = (mote.settings.tsch_slotframeLength / 3 *
                                      mote.settings.tsch_slotDuration)

        # 3. test cell relocation
        # 3.1 increase the following Rpl values in order to avoid invalidating
        # the root as a parent
        mote.rpl.of.MAX_NUM_OF_CONSECUTIVE_FAILURES_WITHOUT_ACK = 100
        mote.rpl.of.UPPER_LIMIT_OF_ACCEPTABLE_ETX = 100
        mote.rpl.of.MAXIMUM_STEP_OF_RANK = 100

        # 3.2 deny input frames over the dedicated cell on the side of the root
        def rxDone_wrapper(self, packet, channel):
            if ((packet is not None) and
                ((self.engine.getAsn() % mote.settings.tsch_slotframeLength)
                 == slot_offset)):
                self.active_cell = None
                self.waitingFor = None
                # silently discard this packet
                return False
            else:
                return self.rxDone_original(packet, channel)

        root.tsch.rxDone_original = root.tsch.rxDone
        root.tsch.rxDone = types.MethodType(rxDone_wrapper, root.tsch)

        # 3.3 run for the next 20 slotframes
        asn_start = sim_engine.getAsn()
        u.run_until_asn(
            sim_engine,
            sim_engine.getAsn() + mote.settings.tsch_slotframeLength * 20)

        # 3.5 RELOCATE should have happened
        logs = [
            log for log in u.read_log_file(filter=['sixp.comp'],
                                           after_asn=asn_start)
            if ((log['_mote_id'] == mote.id) and (
                log['cmd'] == d.SIXP_CMD_RELOCATE))
        ]
        assert len(logs) == 1

        # 4. test dedicated cell deallocation
        # 4.1 stop application packet transmission
        mote.settings.app_pkPeriod = 0

        # 4.2 run for a while
        asn_start = sim_engine.getAsn()
        u.run_until_asn(
            sim_engine,
            sim_engine.getAsn() + mote.settings.tsch_slotframeLength * 20)

        # 4.3 DELETE should have happened
        logs = [
            log for log in u.read_log_file(filter=['sixp.comp'],
                                           after_asn=asn_start)
            if ((log['_mote_id'] == mote.id) and (
                log['cmd'] == d.SIXP_CMD_DELETE))
        ]
        assert len(logs) > 0

        # put the backup value to d.MSF_HOUSEKEEPINGCOLLISION_PERIOD
        d.MSF_HOUSEKEEPINGCOLLISION_PERIOD = msf_housekeeping_period_backup