def test_remove_contact_node():
    graph = ContactGraph()

    identifier1 = ContactIdentifier(from_node='a',
                                    to_node='b',
                                    from_time=0.0,
                                    to_time=10.0,
                                    datarate=1000.0,
                                    delay=0.0)
    graph.add_contact_node(identifier1)
    identifier2 = ContactIdentifier(from_node='b',
                                    to_node='c',
                                    from_time=10.0,
                                    to_time=20.0,
                                    datarate=1000.0,
                                    delay=0.0)
    graph.add_contact_node(identifier2)

    assert len(graph.graph) == 2

    identifier_false = ('a', 'b', 0.0, 10.0, 1000.0, 0.0)
    # Check if exception is thrown if invalid identifier is used
    with pytest.raises(ValueError):
        graph.remove_contact_node(identifier_false)

    assert len(graph.graph) == 2

    graph.remove_contact_node(identifier2)

    # Assert that node was removed properly and both the successor and
    # predecessor lists are empty
    assert len(graph.graph) == 1
    assert not graph.graph[identifier1][0]
    assert not graph.graph[identifier1][1]
示例#2
0
def generate_neighbors(old_neigbors):
    new_neighbors = list()

    for neighbor in old_neigbors:
        tp = list()
        for hops in neighbor[3]:
            tp.append(
                ContactIdentifier(
                    from_node=hops[0],
                    to_node=hops[1],
                    from_time=hops[2],
                    to_time=hops[3],
                    datarate=hops[4],
                    delay=hops[5]))
        route = Route(
            transmission_plan=tp,
            edt=neighbor[1],
            capacity=1000,
            to_time=10000,
            hops=neighbor[2],
            next_hop=ContactIdentifier(*neighbor[0]))
        new_neighbor = Neighbor(
            contact=route.next_hop,
            node_id=route.next_hop.to_node,
            route=route)
        new_neighbors.append(new_neighbor)

    return new_neighbors
def test_reinitialize():
    graph = ContactGraph()

    identifier1 = ContactIdentifier(from_node='a',
                                    to_node='b',
                                    from_time=0.0,
                                    to_time=10.0,
                                    datarate=1000.0,
                                    delay=0.0)
    graph.add_contact_node(identifier1)
    identifier2 = ContactIdentifier(from_node='b',
                                    to_node='c',
                                    from_time=10.0,
                                    to_time=20.0,
                                    datarate=1000.0,
                                    delay=0.0)
    graph.add_contact_node(identifier2)

    assert len(graph.graph) == 2

    graph.reinitialize()

    # Assert that all contacts related to 'b' were removed and the graph is
    # empty
    assert len(graph.graph) == 0
def test_add_contact_node():
    graph = ContactGraph()

    identifier = ('a', 'b', 0.0, 10.0, 1000.0, 0.0)
    # Check if exception is thrown if invalid identifier is used
    with pytest.raises(ValueError):
        graph.add_contact_node(identifier)

    identifier1 = ContactIdentifier(from_node='a',
                                    to_node='b',
                                    from_time=0.0,
                                    to_time=10.0,
                                    datarate=1000.0,
                                    delay=0.0)
    graph.add_contact_node(identifier1)

    assert len(graph.graph) == 1

    identifier2 = ContactIdentifier(from_node='b',
                                    to_node='c',
                                    from_time=10.0,
                                    to_time=20.0,
                                    datarate=1000.0,
                                    delay=0.0)
    graph.add_contact_node(identifier2)

    identifier3 = ContactIdentifier(from_node='d',
                                    to_node='c',
                                    from_time=10.0,
                                    to_time=20.0,
                                    datarate=1000.0,
                                    delay=0.0)
    graph.add_contact_node(identifier3)

    # Check that the added nodes are properly connected
    # (predecessors and successors)
    assert len(graph.graph) == 3
    assert identifier2 in graph.graph[identifier1][0]
    assert len(graph.graph[identifier1][0]) == 1
    assert len(graph.graph[identifier1][1]) == 0
    assert identifier1 in graph.graph[identifier2][1]
    assert len(graph.graph[identifier2][1]) == 1
    assert len(graph.graph[identifier2][0]) == 0
    assert len(graph.graph[identifier3][0]) == 0
    assert len(graph.graph[identifier3][1]) == 0
def test_create_from_contact_plan():
    plan = ContactPlan(1000, 0)

    plan.add_contact('a', 'b', 0.0, 10.0, 1000.0, 0.0, bidirectional=False)
    plan.add_contact('b', 'c', 0.0, 30.0, 1000.0, 0.0, bidirectional=False)
    plan.add_contact('a', 'c', 20.0, 30.0, 1000.0, 0.0, bidirectional=False)

    # Check if exception is thrown if invalid identifier is used
    with pytest.raises(ValueError):
        graph = ContactGraph([])

    graph = ContactGraph(plan)

    assert len(graph.graph) == 6

    id1 = ContactIdentifier('a', 'b', 0.0, 10.0, 1000.0, 0.0)
    id2 = ContactIdentifier('b', 'c', 0.0, 30.0, 1000.0, 0.0)
    id3 = ContactIdentifier('a', 'c', 20.0, 30.0, 1000.0, 0.0)

    assert id2 in graph.graph[id1][0]
    assert id1 in graph.graph[id2][1]
    assert len(graph.graph[id1][0]) == 2
    assert len(graph.graph[id1][1]) == 1
    assert len(graph.graph[id2][0]) == 1
    assert len(graph.graph[id2][1]) == 2
    assert len(graph.graph[id3][0]) == 1
    assert len(graph.graph[id3][1]) == 1

    # Verify number of root node vertices
    assert len(graph.graph[('a', 'a', 0, math.inf, math.inf, 0)][0]) == 2
    assert len(graph.graph[('b', 'b', 0, math.inf, math.inf, 0)][0]) == 1
    assert len(graph.graph[('c', 'c', 0, math.inf, math.inf, 0)][0]) == 0

    # Verify that vertice list of contact nodes contains vertices to correct
    # terminal nodes
    assert ('a', 'a', 0, math.inf, math.inf, 0) in graph.graph[id1][1]
    assert ('b', 'b', 0, math.inf, math.inf, 0) in graph.graph[id1][0]

    assert ('b', 'b', 0, math.inf, math.inf, 0) in graph.graph[id2][1]
    assert ('c', 'c', 0, math.inf, math.inf, 0) in graph.graph[id2][0]

    assert ('a', 'a', 0, math.inf, math.inf, 0) in graph.graph[id3][1]
    assert ('c', 'c', 0, math.inf, math.inf, 0) in graph.graph[id3][0]
示例#6
0
def create_route(old_route):
    plan = list()
    for contact in old_route[0]:
        plan.append(
            ContactIdentifier(
                from_node=contact[0],
                to_node=contact[1],
                from_time=contact[2],
                to_time=contact[3],
                datarate=contact[4],
                delay=contact[5]))

    new_route = Route(
        transmission_plan=plan,
        edt=old_route[1][0],
        capacity=old_route[1][1],
        to_time=old_route[1][2],
        hops=len(old_route[0]),
        next_hop=plan[0])
    return new_route
示例#7
0
def load_route_list(contact_graph, source_node, destination_node,
                    current_time):
    """Generate a list of all feasible routes.

    Routes are calculated from the source node to the destination node.

    Args:
        contact_graph (dict): The topology information in the form of a
            contact graph.
        source_node (string): The identifier of the source node (where the
            routing decision is performed)
        destination_node (type): The identifier of the destination node.
        current_time (int): Time in the simulation when the calculation
            is performed (in ms).

    Raises:
        ValueError: If no matching limit contact can be found due to
            mismatching to_times.

    Returns:
        list: A list of all feasible routes in the form of
        ``(route, route_characteristics)``

    """
    # Initialize route list
    route_list = list()

    # Start by setting the anchorContact to empty
    anchor_contact = None

    # Create an empty list to house all suppressed contacts
    suppressed_contact_list = list()

    # Generate root_contact terminal node definition
    root_contact = ContactIdentifier(
        from_node=source_node,
        to_node=source_node,
        from_time=0,
        to_time=math.inf,
        datarate=math.inf,
        delay=0)
    # Generate bundle's destination terminal node definition
    destination_contact = ContactIdentifier(
        from_node=destination_node,
        to_node=destination_node,
        from_time=0,
        to_time=math.inf,
        datarate=math.inf,
        delay=0)

    # Main loop (which is left when there are no more routes to be considered
    # between the source node and the bundle's destination)
    while True:

        # Determine the shortest route (transmission plan) to the bundle's
        # destination in the contact graph representation of the contact plan
        transmission_plan, distance = dijkstra.get_best_route(
            root_contact,
            destination_contact,
            contact_graph,
            cgr_utils.cgr_neighbor_function,
            current_time,
            suppressed_contacts=suppressed_contact_list,
            hashes=contact_graph.hashes)

        # End the route finding process if no route is found. This means no
        # more routes are available in the contact graph
        if transmission_plan is None:
            break

        # Remove unnecessary root and terminal nodes from the transmission
        # plan
        transmission_plan = transmission_plan[1:-1]

        # Generate route characteristics and add this information to the route
        edt, cap, to_time = cgr_utils.cgr_get_route_characteristics(
            transmission_plan, distance)

        # Extract the first hop of the found route
        first_contact = transmission_plan[0]

        # If the anchor contact is set already, but not to recently found first
        # hop, then iterate over the contact graph (i.e. contact plan) and
        # initialize (and reset the suppress flag for all contact except the
        # ones where the source node is the source)
        if anchor_contact is not None and anchor_contact is not first_contact:
            # Reset the suppress flag for all contacts except the ones
            # where the source node is the source
            for contact in contact_graph.graph.keys():
                if (contact[0] != source_node
                        and contact in suppressed_contact_list):
                    suppressed_contact_list.remove(contact)

            # Supress the anchor node for future iterations of the loop
            suppressed_contact_list.append(anchor_contact)

            anchor_contact = None
            continue

        # Assign the found route as option for the bundle's destination to the
        # route list
        route = Route(
            transmission_plan=transmission_plan,
            next_hop=transmission_plan[0].to_node,
            edt=edt,
            capacity=cap,
            to_time=to_time,
            hops=len(transmission_plan))

        # Add route to route list
        route_list.append(route)

        # If the end time of the overall route is the end time of the first
        # contact of that route (i.e. the first hop), regard to the first
        # contact (hop) as the limiting contact (the contact that ends first
        # and thus renders the entire route invalid)
        if to_time == first_contact.to_time:
            limit_contact = first_contact
        # Otherwise select the first contact as anchor contact and iterate over
        # all remaining hops to determine the limit contact
        else:
            anchor_contact = first_contact
            limit_contact = None
            for contact in transmission_plan:
                if contact.to_time == to_time:
                    limit_contact = contact
                    break

        # Raise error if we couldn't find a contact matching to the calculated
        # to_time
        if limit_contact is None:
            raise ValueError("The calculated to_time of the route does not" +
                             " match any contacts to_time!")

        # Supress the limit contact as we have already found the best route for
        # that limiting contact and worse routes with that limiting contact
        # should not be considered (realised by adding the contact to the
        # suppressed list)
        suppressed_contact_list.append(limit_contact)

    # Return the route list with the updated routes for the bundle's
    # destination node
    return route_list
示例#8
0
def find_critical_bundle_neighbors(contact_graph, source_node,
                                   destination_node, excluded_nodes,
                                   current_time):
    """Determine all feasible neighbor nodes for forwarding a critical bundle.

    Args:
        contact_graph (dict): Topology information as contact graph.
        source_node (string): Node where this routing operation is performed.
        destination_node (string): Destination node of the packet.
        excluded_nodes (type): List of nodes that should not be considered for
            forwarding.
        current_time (int): Time in the simulation that the calculation is
            performed (in ms).

    Returns:
        list: A list of all feasible neighbor nodes in the form `(neighbor,
        distance, hop count, route)`

    """
    # Initialize route list
    neighbor_list = list()

    # Generate root_contact terminal node definition
    root_contact = ContactIdentifier(
        from_node=source_node,
        to_node=source_node,
        from_time=0,
        to_time=math.inf,
        datarate=math.inf,
        delay=0)
    # Generate bundle's destination terminal node definition
    destination_contact = ContactIdentifier(
        from_node=destination_node,
        to_node=destination_node,
        from_time=0,
        to_time=math.inf,
        datarate=math.inf,
        delay=0)

    # Initialize the suppressed contacts list
    suppressed_contacts = []

    for neighbor in contact_graph.graph[root_contact][0]:
        # Ignore neighbors that are in the excluded nodes list
        if neighbor in excluded_nodes:
            continue

        # Determine all neigbors that are ignored for this evaluation run
        blocked_neigbors = copy.deepcopy(contact_graph \
                            .graph[root_contact][0])
        blocked_neigbors.remove(neighbor)
        suppressed_contacts = blocked_neigbors

        # Determine the shortest route to the bundle's destination in the
        # contact graph representation of the contact plan
        transmission_plan, distance = dijkstra.get_best_route(
            root_contact,
            destination_contact,
            contact_graph,
            cgr_utils.cgr_neighbor_function,
            current_time,
            hashes=contact_graph.hashes,
            suppressed_contacts=suppressed_contacts)

        # End the route finding process if no route is found. This means no
        # more routes are available in the contact graph
        if transmission_plan is None:
            continue

        # Remove unnecessary root and terminal nodes from the route
        transmission_plan = transmission_plan[1:-1]

        # Generate route characteristics and add this information to the route
        edt, cap, to_time = cgr_utils.cgr_get_route_characteristics(
            transmission_plan, distance)

        # Assign the found route as option for the bundle's destination to the
        # route list
        route = Route(
            transmission_plan=transmission_plan,
            next_hop=transmission_plan[0].to_node,
            edt=edt,
            capacity=cap,
            to_time=to_time,
            hops=len(transmission_plan))

        # Assign the found route as option for the bundle's destination to the
        # route list
        neighbor_list.append((route.transmission_plan[0], route))

        del blocked_neigbors

    return neighbor_list
示例#9
0
def generate_next_route(contact_graph, source_node, destination_node,
                        excluded_contacts, current_time, avg_rdt):
    """Generate an additional (next best) route to destination.

    Args:
        contact_graph (ContactGraph): A ContactGraph object for topology
            information.
        source_node (string): The node where the routing operation takes place
        destination_node (string): The destinatio node that routes should be
            provided for.
        excluded_contacts (list): A list of contacts that should not be
            considered during the route finding procedure. Used to mimic
            the removal of nodes within the graph in a more memory-efficient
            way.
        current_time (int): The simulated time when the route calculation is
            performed (in ms).
        avg_rdt (recordclass): A recordclass object holding the relevant values
            for determining and updating the dijkstra lookahead window.

    Raises:
        ValueError: If no matching limit contact can be found due to
            mismatching to_times.

    Returns:
        tuple: Provides a list of new routes and a list of excluded
        nodes. The excluded nodes were generated while finding the routes.
        They should not be considered in later searches!

    """
    # Generate root_contact terminal node definition
    root_contact = ContactIdentifier(
        from_node=source_node,
        to_node=source_node,
        from_time=0,
        to_time=math.inf,
        datarate=math.inf,
        delay=0)
    # Generate bundle's destination terminal node definition
    destination_contact = ContactIdentifier(
        from_node=destination_node,
        to_node=destination_node,
        from_time=0,
        to_time=math.inf,
        datarate=math.inf,
        delay=0)

    if avg_rdt.mean >= 0:
        lookahead_time = int(current_time + avg_rdt.mean * 1.2)
    else:
        lookahead_time = 8000

    # Determine the shortest route to the bundle's destination in the
    # contact graph representation of the contact plan with a lookahead
    # window limited to the 120% of the previously observed mean delivery
    # times.
    transmission_plan, distance = dijkstra.get_best_route(
        root_contact,
        destination_contact,
        contact_graph,
        cgr_utils.cgr_neighbor_function,
        current_time,
        hashes=contact_graph.hashes,
        suppressed_contacts=excluded_contacts,
        lookahead_time=lookahead_time)

    # If no route has been found in the lookahead window, fall back to the
    # version without the window.
    if transmission_plan is None:
        # Determine the shortest route to the bundle's destination in the
        # contact graph representation of the contact plan without any
        # lookahead window.
        transmission_plan, distance = dijkstra.get_best_route(
            root_contact,
            destination_contact,
            contact_graph,
            cgr_utils.cgr_neighbor_function,
            current_time,
            hashes=contact_graph.hashes,
            suppressed_contacts=excluded_contacts)

        if transmission_plan is None:
            # End the route finding process if no route is found. This means no
            # more routes are available in the contact graph
            return None, None

        avg_rdt.window_miss += 1
        avg_rdt.count += 1
        avg_rdt.mean += (distance - current_time) / avg_rdt.count
    else:
        avg_rdt.window_hit += 1
        avg_rdt.count += 1
        avg_rdt.mean += (distance - current_time) / avg_rdt.count

    # Remove unnecessary root and terminal nodes from the route
    transmission_plan = transmission_plan[1:-1]

    # Generate route characteristics and add this information to the route
    edt, cap, to_time = cgr_utils.cgr_get_route_characteristics(
        transmission_plan, distance)

    # Assign the found route as option for the bundle's destination to the
    # route list
    route = Route(
        transmission_plan=transmission_plan,
        next_hop=transmission_plan[0].to_node,
        edt=edt,
        capacity=cap,
        to_time=to_time,
        hops=len(transmission_plan))

    # If the end time of the overall route is the end time of the first
    # contact of that route (i.e. the first hop), regard to the first
    # contact (hop) as the limiting contact (the contact that ends first
    # and thus renders the entire route invalid)
    limit_contact = None
    for contact in route.transmission_plan:
        if contact.to_time == route.to_time:
            limit_contact = contact
            new_excluded_contact = limit_contact
            break

    # Raise error if we couldn't find a contact matching to the calculated
    # to_time
    if limit_contact is None:
        raise ValueError("The calculated to_time of the route does not" +
                         " match any contacts to_time!")

    # Supress the limit contact as we have already found the best route for
    # that limiting contact and worse routes with that limiting contact
    # should not be considered (realised by setting the suppressed flag in
    # the working area of the contact)

    # Return the route list with the updated routes for the bundle's
    # destination node
    return route, new_excluded_contact
示例#10
0
def load_route_list(contact_graph, source_node, destination_node,
                    current_time):
    """Generate a list of all feasible routes.

    Routes are calculated from the source node to the destination node.

    Args:
        contact_graph (dict): The topology information in the form of a
                              contact graph.
        source_node (string): The identifier of the source node (where the
                              routing decision is performed)
        destination_node (sting): The identifier of the destination node.
        current_time (int): Time in the simulation when the calculation
            is performed (in ms).

    Raises:
        ValueError: If no matching limit contact can be found due to
            mismatching to_times.

    Returns:
        list: A list of all feasible routes in the form of (route,
              route_characteristics)

    """
    # Initialize route list
    route_list = list()

    # Generate root_contact terminal node definition
    root_contact = ContactIdentifier(
        from_node=source_node,
        to_node=source_node,
        from_time=0,
        to_time=math.inf,
        datarate=math.inf,
        delay=0)
    # Generate bundle's destination terminal node definition
    destination_contact = ContactIdentifier(
        from_node=destination_node,
        to_node=destination_node,
        from_time=0,
        to_time=math.inf,
        datarate=math.inf,
        delay=0)

    # Initialize the list of excluded contacts
    suppressed_contacts = []

    # Main loop (which is left when there are no more routes to be considered
    # between the source node and the bundle's destination)
    while True:

        # Determine the shortest route to the bundle's destination in the
        # contact graph representation of the contact plan
        transmission_plan, distance = dijkstra.get_best_route(
            root_contact,
            destination_contact,
            contact_graph,
            cgr_utils.cgr_neighbor_function,
            current_time,
            hashes=contact_graph.hashes,
            suppressed_contacts=suppressed_contacts)

        # End the route finding process if no route is found. This means no
        # more routes are available in the contact graph
        if transmission_plan is None:
            break

        # Remove unnecessary root and terminal nodes from the route
        transmission_plan = transmission_plan[1:-1]

        # Generate route characteristics and add this information to the route
        edt, cap, to_time = cgr_utils.cgr_get_route_characteristics(
            transmission_plan, distance)

        # Assign the found route as option for the bundle's destination to the
        # route list
        route = Route(
            transmission_plan=transmission_plan,
            next_hop=transmission_plan[0].to_node,
            edt=edt,
            capacity=cap,
            to_time=to_time,
            hops=len(transmission_plan))

        route_list.append(route)

        limit_contact = None
        for contact in route.transmission_plan:
            if contact.to_time == route.to_time:
                limit_contact = contact
                break

        # Raise error if we couldn't find a contact matching to the calculated
        # to_time
        if limit_contact is None:
            raise ValueError("The calculated to_time of the route does not" +
                             " match any contacts to_time!")

        # Remove the limit contact as we have already found the best route for
        # that limiting contact and worse routes with that limiting contact
        # should not be considered. As the temp graph is not used beyond
        # this iterative search, no side effects occur.
        suppressed_contacts.append(limit_contact)

    # Return the route list with the updated routes for the bundle's
    # destination node
    return route_list