def remove_task_from_node(tg, ag, noc_rg, critical_rg, noncritical_rg, task,
                          node, logging):
    """
    Removes a task from TG from a certain Node in AG
    :param tg:  Task Graph
    :param ag:  Architecture Graph
    :param noc_rg:   NoC routing graph
    :param critical_rg:  NoC routing Graph for Critical Section
    :param noncritical_rg:   NoC routing graph for non-Critical Section
    :param task:    Task ID to be removed from Node
    :param node:    Node with Task Mapped on it
    :param logging: logging File
    :return:    True if it removes task with sucess
    """
    if task not in ag.node[node]['PE'].MappedTasks:
        raise ValueError(
            "Trying removing Task from Node which is not the host")

    logging.info("\tREMOVING TASK:" + str(task) + "FROM NODE:" + str(node))
    for edge in tg.edges():
        if task in edge:
            source_node = tg.node[edge[0]]['Node']
            destination_node = tg.node[edge[1]]['Node']
            if source_node is not None and destination_node is not None:
                if source_node != destination_node:
                    # Find the links to be used
                    list_of_links, number_of_paths = Routing.FindRouteInRouteGraph(
                        noc_rg, critical_rg, noncritical_rg, source_node,
                        destination_node, False)
                    if list_of_links is not None:
                        # logging.info("\t\t\tREMOVING PATH FROM NODE:"+str(source_node)+"TO NODE"+str(destination_node))
                        # logging.info("\t\t\tLIST OF LINKS:"+str(list_of_links))
                        for path in list_of_links:
                            for link in path:
                                if edge in ag.edge[link[0]][
                                        link[1]]['MappedTasks'].keys():
                                    del ag.edge[link[0]][
                                        link[1]]['MappedTasks'][edge]
                                    del ag.node[
                                        link[0]]['Router'].MappedTasks[edge]
                                    # logging.info("\t\t\t\tRemoving Packet "+str(edge)+" To Router:"+str(link[0]))
                                    for BatchAndLink in tg.edge[edge[0]][
                                            edge[1]]['Link']:
                                        if BatchAndLink[1] == link:
                                            tg.edge[edge[0]][edge[1]][
                                                'Link'].remove(BatchAndLink)
                            del ag.node[path[len(path) -
                                             1][1]]['Router'].MappedTasks[edge]
                            # logging.info("\t\t\t\tRemoving Packet "+str(edge)+" To Router:"+str(path[len(path)-1][1]))
                    else:
                        logging.warning("\tNOTHING TO BE REMOVED...")
    tg.node[task]['Node'] = None
    ag.node[node]['PE'].MappedTasks.remove(task)
    ag.node[node]['PE'].Utilization -= tg.node[task]['WCET']
    return True
def map_task_to_node(tg, ag, shm, noc_rg, critical_rg, noncritical_rg, task,
                     node, logging):
    """
    Maps a task from Task Graph to a specific Node in Architecture Graph
    :param tg:  Task Graph
    :param ag: Architecture Graph
    :param shm: System Health Map
    :param noc_rg: NoC Routing Graph
    :param critical_rg: NoC Routing Graph for the Cirtical Section
    :param noncritical_rg: NoC Routing graph for non-critical section
    :param task:    Task to be Mapped
    :param node:    Chosen Node for mapping
    :param logging: logging file
    :return: true if can successfully map task to node else returns fails
    """
    if not shm.node[node]['NodeHealth']:
        logging.info("CAN NOT MAP ON BROKEN NODE: " + str(node))
        return False
    elif ag.node[node]['PE'].Dark:
        logging.info("CAN NOT MAP ON DARK NODE: " + str(node))
        return False

    logging.info("\tADDING TASK: " + str(task) + "TO NODE:" + str(node))
    tg.node[task]['Node'] = node
    ag.node[node]['PE'].MappedTasks.append(task)
    ag.node[node]['PE'].Utilization += tg.node[task]['WCET']
    for edge in tg.edges():
        if task in edge:  # find all the edges that are connected to Task
            # logging.info("\t\tEDGE:"+str(edge)+"CONTAINS Task:"+str(task))
            source_node = tg.node[edge[0]]['Node']
            destination_node = tg.node[edge[1]]['Node']
            if source_node is not None and destination_node is not None:  # check if both ends of this edge is mapped
                if source_node != destination_node:
                    # Find the links to be used
                    list_of_links, number_of_paths = Routing.FindRouteInRouteGraph(
                        noc_rg, critical_rg, noncritical_rg, source_node,
                        destination_node, False)
                    # print number_of_paths, list_of_links
                    if list_of_links is not None:
                        # logging.info("\t\t\tADDING PATH FROM NODE:"+str(source_node)+"TO NODE"+str(destination_node))
                        # logging.info("\t\t\tLIST OF LINKS:"+str(list_of_links))
                        counter = 0

                        if tg.edge[edge[0]][edge[1]]["Criticality"] == 'H':
                            probability = 1  # we reserve the whole bandwidth for critical packets...
                        else:
                            probability = 1.0 / number_of_paths

                        for path in list_of_links:
                            for link in path:
                                if edge in ag.edge[link[0]][
                                        link[1]]['MappedTasks'].keys():
                                    ag.edge[link[0]][
                                        link[1]]['MappedTasks'][edge].append(
                                            (counter, probability))
                                    ag.node[link[0]]['Router'].MappedTasks[
                                        edge].append((counter, probability))
                                    # logging.info("\t\t\t\tAdding Packet "+str(edge)+" To Router:"+str(link[0]))
                                else:
                                    ag.edge[link[0]][
                                        link[1]]['MappedTasks'][edge] = [
                                            (counter, probability)
                                        ]
                                    ag.node[link[0]]['Router'].MappedTasks[
                                        edge] = [(counter, probability)]
                                    # logging.info("\t\t\t\tAdding Packet "+str(edge)+" To Router:"+str(link[0]))

                                ag.node[path[len(path) - 1]
                                        [1]]['Router'].MappedTasks[edge] = [
                                            (counter, probability)
                                        ]
                                # logging.info("\t\t\t\tAdding Packet "+str(edge)+" To Router:"+str(path[len(path)-1][1]))

                                edge_list_of_links = list(
                                    batch[1] for batch in tg.edge[edge[0]][
                                        edge[1]]['Link'])
                                if link not in edge_list_of_links:
                                    tg.edge[edge[0]][edge[1]]['Link'].append(
                                        (counter, link, probability))

                            counter += 1
                    else:
                        remove_task_from_node(tg, ag, noc_rg, critical_rg,
                                              noncritical_rg, task, node,
                                              logging)
                        logging.warning("\tNO PATH FOUND FROM " +
                                        str(source_node) + " TO " +
                                        str(destination_node) + "...")
                        print("NO PATH FOUND FROM " + str(source_node) +
                              " TO " + str(destination_node) + " ...")
                        return False
    return True
def remove_cluster_from_node(tg, ctg, ag, noc_rg, critical_rg, noncritical_rg,
                             cluster, node, logging):
    """
    removes a cluster and all its tasks from a certain Node from Architecture Graph(AG)
    :param tg: Task Graph
    :param ctg: Clustered task Graph
    :param ag:  Architecture Graph
    :param noc_rg: NoC Routing Graph
    :param critical_rg: NoC routing Graph of critical Region
    :param noncritical_rg: NoC Routing Graph of non-Critical Region
    :param cluster: ID of The cluster to be mapped
    :param node: ID of the node for mapping the cluster on
    :param logging: logging file
    :return: True if can successfully remove cluster from Node
    """
    logging.info("\tREMOVING CLUSTER:" + str(cluster) + "FROM NODE:" +
                 str(node))
    for ctg_edge in ctg.edges():
        if cluster in ctg_edge:  # find all the edges that are connected to Cluster
            source_node = ctg.node[ctg_edge[0]]['Node']
            destination_node = ctg.node[ctg_edge[1]]['Node']
            if source_node is not None and destination_node is not None:  # check if both ends of this edge is mapped
                if source_node != destination_node:
                    # Find the links to be used
                    list_of_links, number_of_paths = Routing.FindRouteInRouteGraph(
                        noc_rg, critical_rg, noncritical_rg, source_node,
                        destination_node, False)
                    list_of_edges = []
                    if list_of_links is not None:
                        # find all the edges in TaskGraph that contribute to this edge in CTG
                        for tg_edge in tg.edges():
                            if tg.node[tg_edge[0]]['Cluster'] == ctg_edge[0] and \
                                    tg.node[tg_edge[1]]['Cluster'] == ctg_edge[1]:
                                list_of_edges.append(tg_edge)

                    # remove edges from list of edges to all links from list of links
                    if list_of_links is not None and len(list_of_edges) > 0:
                        # logging.info("\t\t\tREMOVING PATH FROM NODE:"+str(source_node)+"TO NODE"+str(destination_node))
                        # logging.info("\t\t\tLIST OF LINKS:"+str(list_of_links))
                        # logging.info("\t\t\tLIST OF EDGES:"+str(list_of_edges))
                        for path in list_of_links:
                            for Link in path:
                                for chosen_edge in list_of_edges:
                                    if chosen_edge in ag.edge[Link[0]][
                                            Link[1]]['MappedTasks'].keys():
                                        del ag.edge[Link[0]][Link[1]][
                                            'MappedTasks'][chosen_edge]
                                        if chosen_edge in ag.node[Link[0]][
                                                'Router'].MappedTasks.keys():
                                            del ag.node[
                                                Link[0]]['Router'].MappedTasks[
                                                    chosen_edge]
                                        # logging.info("\t\t\t\tRemoving Packet "+str(chosen_edge) +
                                        #             " To Router:"+str(Link[0]))
                                        for LinkAndBatch in tg.edge[
                                                chosen_edge[0]][
                                                    chosen_edge[1]]['Link']:
                                            if LinkAndBatch[1] == Link:
                                                tg.edge[chosen_edge[0]][
                                                    chosen_edge[1]][
                                                        'Link'].remove(
                                                            LinkAndBatch)
                            for chosen_edge in list_of_edges:
                                if chosen_edge in ag.node[
                                        path[len(path) -
                                             1][1]]['Router'].MappedTasks:
                                    del ag.node[path[len(path) - 1][1]][
                                        'Router'].MappedTasks[chosen_edge]
                                    # logging.info("\t\t\t\tRemoving Packet "+str(chosen_edge)+" To Router:" +
                                    #             str(path[len(path)-1][1]))
                    else:
                        logging.warning("\tNOTHING TO BE REMOVED...")
    ctg.node[cluster]['Node'] = None
    for task in ctg.node[cluster]['TaskList']:
        tg.node[task]['Node'] = None
        ag.node[node]['PE'].MappedTasks.remove(task)
    ag.node[node]['PE'].Utilization -= ctg.node[cluster]['Utilization']
    return True
def add_cluster_to_node(tg, ctg, ag, shm, noc_rg, critical_rg, noncritical_rg,
                        cluster, node, logging):
    """
    Adds a Cluster from CTG and all its Task to a Node from Architecture Graph
    :param tg:  Task Graph
    :param ctg: Clustered Task Graph
    :param ag:  Architecture Graph
    :param shm: System Health Map
    :param noc_rg: NoC Routing Graph
    :param critical_rg: NoC Routing Graph for Critical region
    :param noncritical_rg: NoC routing Graph for Non-Critical Region
    :param cluster: ID Cluster to be mapped
    :param node: ID of the Node for mapping cluster on
    :param logging: logging file
    :return: True if maps the cluster successfully otherwise False
    """
    if not shm.node[node]['NodeHealth']:
        logging.info("CAN NOT MAP ON BROKEN NODE: " + str(node))
        return False
    elif ag.node[node]['PE'].Dark:
        logging.info("CAN NOT MAP ON DARK NODE: " + str(node))
        return False

    # Adding The cluster to Node...
    logging.info("\tADDING CLUSTER: " + str(cluster) + "TO NODE:" + str(node))
    ctg.node[cluster]['Node'] = node
    for Task in ctg.node[cluster]['TaskList']:
        tg.node[Task]['Node'] = node
        ag.node[node]['PE'].MappedTasks.append(Task)
    ag.node[node]['PE'].Utilization += ctg.node[cluster]['Utilization']

    for ctg_edge in ctg.edges():
        if cluster in ctg_edge:  # find all the edges that are connected to cluster
            # logging.info("\t\tEDGE:"+str(ctg_edge)+"CONTAINS CLUSTER:"+str(cluster))
            source_node = ctg.node[ctg_edge[0]]['Node']
            destination_node = ctg.node[ctg_edge[1]]['Node']
            if source_node is not None and destination_node is not None:  # check if both ends of this edge is mapped
                if source_node != destination_node:
                    list_of_links, number_of_paths = Routing.FindRouteInRouteGraph(
                        noc_rg, critical_rg, noncritical_rg, source_node,
                        destination_node, False)  # Find the links to be used
                    list_of_edges = []
                    # print ("number_of_paths:", number_of_paths)
                    # print number_of_paths, list_of_links
                    if list_of_links is not None:
                        # find all the edges in TaskGraph that contribute to this edge in CTG
                        for tg_edge in tg.edges():
                            if tg.node[tg_edge[0]]['Cluster'] == ctg_edge[0] and \
                                    tg.node[tg_edge[1]]['Cluster'] == ctg_edge[1]:
                                list_of_edges.append(tg_edge)
                    # print ("LIST OF LINKS:", list_of_links)
                    # add edges from list of edges to all links from list of links
                    # todo: I have to think more... this is not enough to add all the links there...
                    if list_of_links is not None and len(list_of_edges) > 0:
                        # logging.info("\t\t\tADDING PATH FROM NODE:"+str(source_node)+"TO NODE"+str(destination_node))
                        # logging.info("\t\t\tLIST OF LINKS:"+str(list_of_links))
                        # logging.info("\t\t\tLIST OF EDGES:"+str(list_of_edges))
                        counter = 0
                        for path in list_of_links:
                            for link in path:
                                for chosen_edge in list_of_edges:
                                    if tg.edge[chosen_edge[0]][chosen_edge[1]][
                                            "Criticality"] == 'H':
                                        probability = 1  # we reserve the whole bandwidth for critical packets...
                                    else:
                                        probability = 1.0 / number_of_paths

                                    if chosen_edge in ag.edge[link[0]][
                                            link[1]]['MappedTasks'].keys():
                                        ag.edge[link[0]][link[1]][
                                            'MappedTasks'][chosen_edge].append(
                                                (counter, probability))
                                        ag.node[link[0]]['Router'].MappedTasks[
                                            chosen_edge].append(
                                                (counter, probability))
                                        # logging.info("\t\t\t\tAdding Packet "+str(chosen_edge)+" To Router:" +
                                        #              str(link[0]))
                                    else:
                                        ag.edge[link[0]][link[1]][
                                            'MappedTasks'][chosen_edge] = [
                                                (counter, probability)
                                            ]
                                        ag.node[link[0]]['Router'].MappedTasks[
                                            chosen_edge] = [(counter,
                                                             probability)]
                                        # logging.info("\t\t\t\tAdding Packet "+str(chosen_edge)+" To Router:" +
                                        #              str(link[0]))
                                    edge_list_of_links = list(
                                        batch[1]
                                        for batch in tg.edge[chosen_edge[0]][
                                            chosen_edge[1]]['Link'])
                                    if link not in edge_list_of_links:
                                        tg.edge[chosen_edge[0]][
                                            chosen_edge[1]]['Link'].append(
                                                (counter, link, probability))

                            for chosen_edge in list_of_edges:
                                if tg.edge[chosen_edge[0]][
                                        chosen_edge[1]]["Criticality"] == 'H':
                                    probability = 1  # we reserve the whole bandwidth for critical packets...
                                else:
                                    probability = 1.0 / number_of_paths
                                ag.node[path[len(path)-1][1]]['Router'].MappedTasks[chosen_edge] = \
                                    [(counter, probability)]
                                # logging.info("\t\t\t\tAdding Packet "+str(chosen_edge)+" To Router:" +
                                #             str(path[len(path)-1][1]))
                            counter += 1
                    else:
                        logging.warning(
                            "\tNO PATH FOUND FROM SOURCE TO DESTINATION...")
                        logging.info(
                            "REMOVING ALL THE MAPPED CONNECTIONS FOR CLUSTER "
                            + str(cluster))
                        remove_cluster_from_node(tg, ctg, ag, noc_rg,
                                                 critical_rg, noncritical_rg,
                                                 cluster, node, logging)
                        return False
    return True