# -*- coding: utf-8 -*-
"""

Autor: Juan Pablo Astudillo Leon
supervisor: Dr. Ing. Jose Ignacio Alvarez-Hamelin

Software desarrollado en el Grupo de redes complejas y comunicacion de datos
Facultad de Ingenieria
Universidad de Buenos Aires

Este archivo contiene 5 funciones:

1. crearNodos(numNodosTotales) : Toma los archivos generados en Omnet++ 
    y los combina para generar un archivo por nodo donde se cada archivo almacena
    el trayecto de los paquetes
                                
2. ordenarNodos(numNodosTotales) :  Ordena los archivos de los nodos, para facilitar el analisis

3. separarImagenes(numNodosTotales) :  Divide los archivos de los nodos por imagenes para facilitar el analisis

4. hashComputo=leerCGRComputo(nodSat) : Calcula el computo de CGR en los nodos

5. hashSDR=leerSDR(nodSat,ymax1=70,ymax2=32) : Calcula paquetes descartados por falta de almacenamiento

"""

def crearNodos(
        numNodosTotales):  
    # Crea archivos que contiene el recorrido de los bundles desde el nodo origen a destino
    
    print('*****Comienza obtener trayecto de los paquetes por nodo')
    pathArchivosNodos = './Nodos/'
    
    # Tabla Hash que almacena el directorio de los archivos
    archivosNodos = {} 
    
    for i in range(numNodosTotales):
        idNodo = i + 1
        f = open('./BundlesOmnet/bundleNodo' + str(idNodo) + '.txt', 'r')
        for linea in f:
            cadenaSeparada = linea.split(',')
            auxArchivoNodo = pathArchivosNodos + 'nodo' + cadenaSeparada[1]
            
            #=============Para guardar los nodos
            # Verifica si el archivo a escribir  ya esta abierto
            if archivosNodos.get(auxArchivoNodo, -1) == -1: 
                archivosNodos[auxArchivoNodo] = open(auxArchivoNodo + '.txt', 'w')
                
            archivosNodos[auxArchivoNodo].write(linea)
        print('Archivo bundleNodo' + str(idNodo) + ' completado')
        print((datetime.datetime.today()))
        f.close()
    print('*****Termina obtener trayecto de los paquetes por nodo')

def ordenarNodos(numNodosTotales):  
    # Ordena los nodos

    print('*****Comienza ordenar los nodos')
    
    # Directorio en donde se almacenaran los archivos    
    pathArchivosNodos = './Nodos/'
    
    for i in range(numNodosTotales):
        idNodo = i + 1
        auxArchivo = pathArchivosNodos + 'nodo' + str(idNodo)+ '.txt'
        numLineas = numeroLineasArchivo(auxArchivo)
        matriz = crearMatriz(numLineas, 8)
        f = open(auxArchivo, 'r')        
        # Se cargan los archivos en una matriz antes de ordenar
        cont = 0
        for linea in f:
            cadenaSeparada = linea.split(',')
            matriz[cont][0] = int(cadenaSeparada[0])
            matriz[cont][1] = float(cadenaSeparada[1])
            matriz[cont][2] = float(cadenaSeparada[2])
            matriz[cont][3] = float(cadenaSeparada[3])
            matriz[cont][4] = float(cadenaSeparada[4])
            matriz[cont][5] = float(cadenaSeparada[5])
            matriz[cont][6] = float(cadenaSeparada[6])
            matriz[cont][7] = float(cadenaSeparada[7])
            cont = cont + 1
        f.close()
        
        # Ordena los bundles por los tiempos de simulacion
        matriz = sorted(matriz, key=lambda it: it[0])  
        
        # Guardan los archivos ordenados     
        file = open(auxArchivo, 'w')
        for cont in range(numLineas):
            uno = str(matriz[cont][0])+ ','
            dos = str(matriz[cont][1]) + ','
            tres = str(matriz[cont][2]) + ','
            cuatro = str(matriz[cont][3]) + ','
            cinco = str(matriz[cont][4]) + ','
            seis = str(matriz[cont][5]) + ','
            siete = str(matriz[cont][6]) + ','
            ocho = str(matriz[cont][7]) + ','
            linea = uno + dos + tres + cuatro + cinco + seis + siete + ocho + '\n'
            file.write(linea)
        file.close()
        print('Archivo Nodo' + str(idNodo)+ ' fue ordenado')
        print((datetime.datetime.today()))
    print('*****Termina ordenar los nodos')


def numeroLineasArchivo(auxArchivo):  
    # Obtiene el numero de lineas de un archivo
    file = open(auxArchivo, 'r')
    numLineas = file.readlines()
    numLineas = len(numLineas)
    file.close()
    return numLineas

def crearMatriz(numero_filas, numero_columnas):  
    # Crea una matriz con algunas dimensiones
    matriz = []
    for i in range(numero_filas):
        matriz.append([])
        for j in range(numero_columnas):
            matriz[i].append(None)
    return matriz

def imprimirConsola(dato): 
    #Para facilitar la impresion
    for linea in dato:
        print(linea)

def separarImagenes(numNodosTotales): 
    # Separa las imagenes en diferentes archivos
    print('******Comienza la separacion de las imagenes')
    for cont in range(numNodosTotales - 1): 
        idNodo = cont + 1
        pathArchivosNodos = './Nodos/'
        f = open('./Nodos/nodo' + str(idNodo) + '.txt', 'r')
        archivosNodosImagen = {}
        
        # Tabla Hash que almacena los identificadores de los bundles
        bundle = {} 

        for linea in f:
            cadenaSeparada = linea.split(',')
            idBundle = cadenaSeparada[0]
            nodoSource = cadenaSeparada[1][:-2]
            idImagen = cadenaSeparada[7][:-2]
            
            #El nombre del archivo creado contiene el idNodo y el identificador de la imagen
            auxArchivoNodoImagen = pathArchivosNodos + 'nodo' + nodoSource + 'imagen' + idImagen
            
            #===Para guardar las imagenes por nodo
            
            # Verifica si esta creado y abierto un archivo que identifica la imagen con respecto al nodo
            if archivosNodosImagen.get(auxArchivoNodoImagen, -1) == -1:
                archivosNodosImagen[auxArchivoNodoImagen] = open(auxArchivoNodoImagen + '.txt', 'w')
            if bundle.get(idBundle, -1) == -1:
                bundle[idBundle] = 0
            if bundle[idBundle] != cadenaSeparada[3]:
                bundle[idBundle] = cadenaSeparada[3]
                archivosNodosImagen[auxArchivoNodoImagen].write(linea)
        f.close()
        print('Nodo ' + nodoSource + ' Finalizado')
        print((datetime.datetime.today()))

def leerCGRComputo(
        numNodosTotales):  
    # Calcula el computo de CGR en los nodos
    print('*****Comienza Programa Computo de CGR')
    
    hashContador={} # Almacena los computos de CGR por cada nodo
    computo=0 # Almacena el computo total de CGR en la simulacion
    
    for i in range(numNodosTotales):
        idNodo = i + 1
        
        # Directorio de los archivos donde se almacena el computo de CGR en los nodos
        f = open('./BundlesOmnet/calculoCGR' + str(idNodo) + '.txt', 'r')
        for linea in f:
            cadenaSeparada = linea.split(',')
            computoCGR=int(cadenaSeparada[8])
            auxBundleNodo='LEO'+str(idNodo)
            if hashContador.get( auxBundleNodo, -1) == -1:
                hashContador[auxBundleNodo]=[]
            hashContador[auxBundleNodo].append(computoCGR)
            computo=computo+computoCGR
        f.close()
    print ('CGR fue ejecutado '+str(computo)+ ' veces' )

    #===========GRAFICA DE LOS RESULTADOS==========================================

    pl.figure()
    # Grafica del diagrama de cajas de CGR por cada nodo
    box=pl.boxplot([hashContador['LEO1'],hashContador['LEO2'],hashContador['LEO3'],
                    hashContador['LEO4'],hashContador['LEO5']],
                    notch=False, patch_artist=True, sym='')
    # Colorear los boxplots
    colors = ['cyan', 'lightblue', 'lightgreen', 'tan', 'pink']
    for patch, color in zip(box['boxes'], colors):
        patch.set_facecolor(color)
    # Agregar informacion sobre la grafica    
    pl.title(r'$\mathsf{C\'alculo \ CGR \ por \ cada \ paquete  \ en \ cada \ nodo}$')
    pl.xlabel(r'$\mathsf{Nodos \ satelitales}$')
    pl.ylabel(r'$\mathsf{N\'umero \ de \ CGRs \ por \ paquete}$')
    pl.xticks([0,1,2,3,4,5],['',r'$\mathsf{LEO1}$',r'$\mathsf{LEO2}$',r'$\mathsf{LEO3}$'
                            ,r'$\mathsf{LEO4}$',r'$\mathsf{LEO5}$'])
    pl.grid()
    pl.show()
    
    print((datetime.datetime.today()))
    print('*****Fin Programa Computo de CGR')
    return hashContador

def leerSDR(
        numNodosTotales,ymax1,ymax2):  
    # Calcula la cantidad de paquetes rechazados por los nodos cuando se supera la capacidad
    # de almacenamiento
    
    print('*****Analizar paquetes rechazados por falta de almacenamiento')
    hashContador={} # Almacena los valores de los bundles eliminados
    for i in range(numNodosTotales):
        idNodo = i + 1
        
        # Directorio de los archivos a calcular
        f = open('./BundlesOmnet/bundleSDR' + str(idNodo) + '.txt', 'r')
        for linea in f:
            cadenaSeparada = linea.split(',')
            idBundleNodo=(cadenaSeparada[1])
            auxBundleNodo='bundleSDR'+str(idNodo)+'nodo'+idBundleNodo
            if hashContador.get( auxBundleNodo, -1) == -1:
                hashContador[auxBundleNodo]=0
            hashContador[auxBundleNodo]=hashContador[auxBundleNodo]+1
        f.close()
    
    # Calculo de los valores    
    matrizBarra=[]
    maximo=0
    for i in range(numNodosTotales):
        auxBarra=[]
        for j in range (numNodosTotales):
            auxBundleNodo='bundleSDR'+str(i+1)+'nodo'+str(j+1)
            if hashContador.get(auxBundleNodo, -1) == -1:
                hashContador[auxBundleNodo]=0
            auxBarra.append(hashContador[auxBundleNodo])
            if max(auxBarra)>maximo:
                maximo=max(auxBarra)
        matrizBarra.append(auxBarra)

    #print ('')
    total=0
    for i in range(len(matrizBarra)):
        total=total+sum(matrizBarra[i])

    print ('Se descartaron un total de : '+ str(total) + ' paquetes a la entrada')
    #print('')
    # Se realiza las graficas y calculos solamente en el caso que se hayan descartado
    # paquetes
    if total>0:
        for i in range(numNodosTotales):
            cont=0
            # Cambiar los resultados a porcentajes 
            for j in range (numNodosTotales):
                cont=cont+matrizBarra[j][i]
            # Imprime los primeros resultados    
            print ('Se descartaron el: '+ "%.2f" % (float(cont)/float(total)*100) +'% de paquetes generados por el nodo LEO'+str(i+1))
        cont=0
        for i in range(len(matrizBarra)):
            cont=cont+sum(matrizBarra[i])
        #print('')
        auxBarraTotales=[]
        for i in range(len(matrizBarra)):
            # Imprime los segundos resultados
            print ('LEO'+str(i+1)+' descarto: '
            +"%.2f" % (float(sum(matrizBarra[i]))/float(cont)*100)+'% de los paquetes a la entrada')
            auxBarraTotales.append(float (sum(matrizBarra[i]))/float(cont)*100)
        # Se calcula la matriz tranpuesta para poder graficar correctamente
        matrizBarra=matrizTranspuesta(matrizBarra)

        #=============Graficas de los resultados==============================
        f=pl.subplots()

        pl.subplot(1,2,1)
        X = np.arange(5)
        maximo=0
        for i in range(len(matrizBarra)):
            suma=sum(matrizBarra[i])
            for j in range(len(matrizBarra)):
                matrizBarra[i][j]=(float(matrizBarra[i][j])/float(suma))*100
                if matrizBarra[i][j]>maximo:
                    maximo=matrizBarra[i][j]
        pl.bar(X + 0.0, matrizBarra[0], color = "b", width = 0.15, alpha=0.4,label=r'$\mathsf{LEO1}$')
        pl.bar(X + 0.15, matrizBarra[1], color = "g", width = 0.15, alpha=0.4,label=r'$\mathsf{LEO2}$')
        pl.bar(X + 0.30, matrizBarra[2], color = "r", width = 0.15, alpha=0.4,label=r'$\mathsf{LEO3}$')
        pl.bar(X + 0.45, matrizBarra[3], color = "y", width = 0.15, alpha=0.4,label=r'$\mathsf{LEO4}$')
        pl.bar(X + 0.60, matrizBarra[4], color = "r", width = 0.15, alpha=0.8,label=r'$\mathsf{LEO5}$')
        pl.xticks(X+0.38, [r'$\mathsf{LEO1}$',r'$\mathsf{LEO2}$',
                               r'$\mathsf{LEO3}$',r'$\mathsf{LEO4}$',r'$\mathsf{LEO5}$'])
        pl.legend()
        pl.title(r'$\mathsf{Distribuci\'on \ de \ paquetes \ descartados}$')
        pl.xlabel(r'$\mathsf{Nodos}$')
        pl.ylabel(r'$\mathsf{Cantidad \ de \ paquetes \ [\%]}$')
        # Para configurar el limite superior de la grafica de la izquierda
        pl.ylim(0, ymax1)
        pl.yticks([0,10,20,30,40])
        pl.grid()


        pl.subplot(1,2,2)
        X = np.arange(5)
        bar_width = 0.55
        opacity = 0.4
        error_config = {'ecolor': '0.3'}
        pl.bar(X+0.1, auxBarraTotales, bar_width,
                         alpha=opacity,
                         color='g',
                         error_kw=error_config,
                         label=r'$\mathsf{Nodos}$')
        pl.xticks(X+0.38, [r'$\mathsf{LEO1}$',r'$\mathsf{LEO2}$',
                               r'$\mathsf{LEO3}$',r'$\mathsf{LEO4}$',r'$\mathsf{LEO5}$'])
        # Incluir informacion en las graficas
        pl.legend()
        pl.xlabel(r'$\mathsf{Nodos}$')
        pl.ylabel(r'$\mathsf{Cantidad \ de \ paquetes [\%]}$')
        pl.title(r'$\mathsf{Nodo \ que \ descart\'o \ m\'as \ paquetes \ a \ la \ entrada}$')
        # Para configurar el limite superior de la grafica de la derecha
        pl.ylim(0, ymax2)
        pl.grid()
        # PAra colocar el valor calculado sobre las barras
        for x, y in zip(X, auxBarraTotales):
            pl.text(x + 0.40, y + 0.05, r'$\mathsf{%.1f}$' % y +'%', ha='center', va='bottom', fontsize=21)
    
    print((datetime.datetime.today()))
    print('*****Fin analizar paquetes rechazados por falta de almacenamiento')
    return hashContador

def matrizTranspuesta(anArray):
    transpuesta = [None]*len(anArray[0])
    for t in range(len(anArray)):
        transpuesta[t] = [None]*len(anArray)
        for tt in range(len(anArray[t])):
            transpuesta[t][tt] = anArray[tt][t]
    return transpuesta

#=========================INICIO DEL PROGRAMA==================================

#============================BIBLIOTECAS=======================================
import datetime
import matplotlib.pyplot as pl
import numpy as np
#==========OPCIONES DE LAS GRAFICAS============================================
#Modificar para cambiar el tamano de la letra
tamanoFuente=29
pl.rcParams['axes.titlesize'] = tamanoFuente
pl.rcParams['axes.labelsize'] = tamanoFuente
pl.rcParams['legend.fontsize'] = tamanoFuente-3.5
pl.rcParams['lines.linewidth'] = 4
pl.rcParams['axes.grid'] = 'off'
pl.rcParams['xtick.labelsize'] = tamanoFuente
pl.rcParams['ytick.labelsize'] = tamanoFuente
pl.rcParams['xtick.major.size'] = 1
#===========VARIABLES DEL PROGRAMA=============================================
print('*****Comienza el procesamiento de datos')
print((datetime.datetime.today()))
numNodosTotales = 6 #Incluye la estacion terrena
nodSat=numNodosTotales-1
#==============================================================================
crearNodos(numNodosTotales)    # Toma los archivos generados en Omnet y los combina para generar un archivo por nodo
                                # el cual contiene el path de los bundles desde el nodo origen al destino
ordenarNodos(numNodosTotales)  # Ordena los archivos de los nodos, para facilitar el analisis
separarImagenes(numNodosTotales)   #Divide los archivos de los nodos por imagenes para facilitar el analisis
hashComputo=leerCGRComputo(nodSat) # Calcula el computo de CGR en los nodos
hashSDR=leerSDR(nodSat,ymax1=70,ymax2=32)  # Bundles descartados por falta de almacenamiento
print('*****Finaliza el procesamiento de datos')













