#!/usr/bin/python
# -*- coding: utf-8 -*-
# Lionel GAUTHIER
# This script is a downgrade of a python script written for the openair stack, generating sequence diagrams charts, displaying
# messages exchanged between LTE access stratum protocol entities.
# This script needs mscgen tool (http://www.mcternan.me.uk/mscgen/).
#  
# The aim of this script is to collect some traces from oaisim stack and generate a mscgen script, then generate a sequence diagram 
# image (png or jpeg) based on the scripts generated.
# An example of how it can be invoqued: msc_gen.py /tmp/msc_log.txt /tmp/msc_log2.txt  msc_logx.txt are input text files,
# They/It are/is generated by the execution of executables in linux user space. May be it is better to operate on log files 'grepped' with MSC:
#  ./my_exe.exe | grep \[MSC > /tmp/msc_log1.txt 


# Now, examples of how to generate a mscgen trace in a C executable:
#  -First you have to declare the instance(s) of the protocol(s) that will send or receive messages.
#    This is done with the following line:
#       [MSC_NEW][system time][protocol_name=instance_name] 
#       examples:
#           printf("[MSC_NEW][%s][MIH-F=%s]\n", getTimeStamp4Log(), g_mihf_id); for declaring a new instance of a protocol entity of type MIH-F
#                                                                           with an instance name g_mihf_id (of type char*)
#           printf("[MSC_NEW][%s][NAS=%s]\n", getTimeStamp4Log(), "nas");  other example.
#    This declaration of instances of protocols is mandatory to for this script to work.
#
#  -Then for tracing a message you have to log a line following this format:
#       [MSC_MSG][system time][source instance_name][--- message_name --->][destination instance_name]
#       Note:
#          ---> means a message that has been received correctly by destination
#          ---x means a message lost by destination
#       example
#       NOTICE("[MSC_MSG][%s][%s][--- Link_Register.indication\\n%s ---x][%s]\n",getTimeStamp4Log(),g_link_id,g_msc_gen_buf,g_mihf_id);
#




import sys
import subprocess
import re
import socket
import datetime
import os.path
from datetime import date

MSCGEN_OUTPUT_TYPE       = "png"
MAX_MESSAGES_PER_PAGE    = 36

SYSTEM_FRAME_NUMBER_STR  = 'FRAME'
MODULE_STR               = 'MOD'
RADIO_BEARER_STR         = 'RB'
MSC_NEW_STR              = '[MSC_NEW]'
MSC_MSG_STR              = '[MSC_MSG]'
MSC_NBOX_STR             = '[MSC_NBOX]'
MSC_BOX_STR              = '[MSC_BOX]'
MSC_ABOX_STR             = '[MSC_ABOX]'
MSC_RBOX_STR             = '[MSC_RBOX]'

# This dic is filled as follow : g_entities_dic[protocol_instance_name].append(protocol_name)
# where protocol_name can be any of the following MI-USER, MIH-F, RAL, LINK-SAP
g_entities_dic = {}
g_reverse_entities_dic = {}


# g_messages is filled with dictionnaries:
# message_dic['entity_src'] = protocol_entity_src
# message_dic['entity_dst'] = protocol_entity_dest
# message_dic['msg']        = message
# message_dic['line_color'] = g_display_color[entity_name]
# message_dic['text_color'] = g_display_color[entity_name]
# message_dic['time']       = system_frame_number
# message_dic['seq_no']     = sequence_number_generator()
# g_messages.append(message_dic)
g_messages = []
g_messages_dic = {}


# g_display_order_dic is a dictionnary where the order of the display of entities sharing the same module identifier is hardcoded
#MAC and PHY valus have to be above num max radio bearer value
#g_display_order_dic  = {'MIH-USER': 0, 'MIH-F': 1, 'RAL': 3, 'NAS': 4}
g_display_order_dic  = {'NAS-UE': 0, 'AS': 1, 'NAS-MME': 3}

# Display color of messages of sending entities
g_display_color  = {'IP':	'\"teal\"',
                    'RRC_UE':	'\"red\"',
                    'MIH-USER':	'\"black\"',
                    'MIH-F':	'\"black\"',
                    'RAL':	'\"black\"',
                    'RAL_M':	'\"black\"',
                    'RLC_UM':	'\"navy\"',
                    'RLC_TM':	'\"navy\"',
                    'NAS':	'\"black\"',
                    'MAC_eNB':	'\"indigo\"',
                    'PHY':	'\"purple\"',
                    'NAS-UE':	'\"black\"',
                    'NAS-MME':	'\"black\"',
                    'AS':	'\"black\"'}


g_final_display_order_list = []
g_sequence_generator = 0

def sequence_number_generator():
    global g_sequence_generator
    l_seq = g_sequence_generator
    g_sequence_generator = g_sequence_generator + 1
    return l_seq

def parse_log_file_for_discovering_protocol_entities(filename):
    global g_entities_dic
    global g_entities
    global g_messages
    global g_final_display_order_list
    #open TXT file that contain OAI filtered traces for mscgen

    fhandle  = open(filename, 'r')
    fcontent = fhandle.read()
    fhandle.close()

    # split file content in lines
    lines = fcontent.splitlines()
    for line in lines:
        system_time = 'unknown'

        # if line is a trace of the creation of a new protocol instance
        if MSC_NEW_STR in line:
            partition = line.rpartition(MSC_NEW_STR)
            msc_log_string = partition[2]
            #print (" %s " % msc_log_string)
            partition = msc_log_string.split('[')
            print ("\n\n %s \n" % partition)
            #if len(partition) == 2:

            item = partition[1]
            item = item.strip()
            item = item.strip(']')
            system_time = item.split(' ')[-1]
            print ("NEW system_time  %s " % system_time)

            item = partition[2]
            item = item.strip()
            item = item.strip(']')
            protocol_entity_type = item.split('=')[0]
            protocol_entity_name = item.split('=')[-1]
            print ("NEW protocol_entity_type  %s " % protocol_entity_type)
            print ("NEW protocol_entity_name  %s " % protocol_entity_name)

            if protocol_entity_name not in g_entities_dic:
                g_entities_dic[protocol_entity_name] = protocol_entity_type

            if protocol_entity_type not in g_reverse_entities_dic:
                g_reverse_entities_dic[protocol_entity_type] = protocol_entity_name
            else:
                entity_name_list = g_reverse_entities_dic[protocol_entity_type]
                entity_name_list.append(protocol_entity_name)
                g_reverse_entities_dic[protocol_entity_type] = entity_name_list
            #print (" g_entities_dic[%d][%d].append(%s)" % (module_id_int, radio_bearer_id_int, entity_name_src))
            print (" g_entities_dic= %s\n\n" % (g_entities_dic))
            print (" g_reverse_entities_dic= %s\n\n" % (g_reverse_entities_dic))

            #g_entities.append(protocol_entity_type)
            #print (" %s \n" % protocol_entity)





def parse_log_file(filename):
    global g_entities_dic
    global g_entities
    global g_messages
    global g_final_display_order_list
    #open TXT file that contain OAI filtered traces for mscgen
    fhandle  = open(filename, 'r')
    fcontent = fhandle.read()
    fhandle.close()

    # split file content in lines
    lines = fcontent.splitlines()
    for line in lines:
        system_time           = 'unknown'
        message               = 'unknown'
        entity_name_src       = 'unknown'
        entity_name_dest      = 'unknown'


        # if line is a trace of the creation of a new protocol instance
        if MSC_MSG_STR in line:
            partition = line.strip().rpartition(MSC_MSG_STR)
            msc_log_string = partition[2].strip()
            print (" ++++ %s " % msc_log_string)
            partition = msc_log_string.split('[')
            print (" ++++ %s " % partition)

            #if len(partition) == 9:
            message_dic = {}

            system_time  = partition[1].strip().split(' ')[-1].strip(']')
            message_dic['display_time']    = system_time
            seconds_int       = int(system_time.split(':')[0])
            micro_seconds_int = int(system_time.split(':')[-1])
            system_time_int = seconds_int*1000000 + micro_seconds_int
            system_time=str(system_time_int)
            print ('system_time %s' % system_time)

            entity_name_src      = partition[2].strip().split(' ')[-1].strip(']')
            print ('entity_name_src %s' % entity_name_src)

            message              = partition[3].strip().strip(']').strip()
            if (message[-1] == 'x') or (message[-1] == 'X'):
                message_dic['arc']  = '-x'
            elif message[-1] == '>':
                message_dic['arc']  = '=>'
            message = message.strip('<').strip('>').strip('x').strip('X').strip('-').strip('=').strip()
            print ('message %s' % message)

            entity_name_dest      = partition[4].strip().split(' ')[-1].strip(']')
            print ('entity_name_dest %s' % entity_name_dest)
            message_dic['entity_src']      = entity_name_src
            message_dic['entity_dst']      = entity_name_dest
            message_dic['msg']             = message
            message_dic['line_color']      = g_display_color[g_entities_dic[entity_name_src]]
            message_dic['text_color']      = g_display_color[g_entities_dic[entity_name_src]]
            message_dic['time']            = system_time
            #message_dic['seq_no']          = sequence_number_generator()
            print ('%s\n\n' % message_dic)
            g_messages_dic[system_time_int] = message_dic
            #g_messages.append(message_dic)

#TODO !!!
        if MSC_RBOX_STR in line:
            partition = line.strip().rpartition(MSC_RBOX_STR)
            msc_log_string = partition[2].strip()
            print (" ==== %s " % msc_log_string)
            partition = msc_log_string.split('[')
            print (" ==== %s " % partition)
#TODO !!!

def generate_sorted_message_list():
    first_time_stamp = 0
    for system_time_int in sorted(g_messages_dic.iterkeys()):
        if first_time_stamp == 0:
            first_time_stamp = system_time_int
        message_dic = g_messages_dic[system_time_int]
        message_dic['seq_no'] = sequence_number_generator()
        message_dic['display_time'] = str(system_time_int - first_time_stamp)
        g_messages.append(message_dic)

def generate_sorted_entity_display_list():
    module_display_order_dic = {}
    for entity_name in sorted(g_entities_dic.iterkeys()):
        module_display_order_dic[g_display_order_dic[g_entities_dic[entity_name]]] = entity_name


    print("------------------------------------")
    print("  %s " % (module_display_order_dic))

    for display_priority in sorted(module_display_order_dic.iterkeys()):
        g_final_display_order_list.append(module_display_order_dic[display_priority])

def msc_chart_write_header(fileP):
    global g_final_display_order_list
    fileP.write("msc {\n")
    fileP.write("width = \"2048\";\n")

    entity_line_list_str = ''
    for entity in g_final_display_order_list:
        entity_line_list_str = entity_line_list_str + ' ' + entity + ','

    entity_line_list_str = entity_line_list_str.rstrip().strip(',')
    fileP.write("  %s;" % (entity_line_list_str))


def msc_chart_write_footer(fileP):
    fileP.write("\n}\n")

def msc_chart_generate(file_nameP):
    global  MSCGEN_OUTPUT_TYPE
    command_line = "mscgen -T " + MSCGEN_OUTPUT_TYPE + " -i " + file_nameP + "; chmod 777 " +file_nameP + ";"

    print("Command Line:  %s " % (command_line))
    fi,fo,fe=os.popen3(command_line)
    for i in fe.readlines():
        print "error:",i

def get_nem_file_descriptor():
    global g_base_file_name
    global g_page_index
    l_file_name = g_base_file_name + str(g_page_index)+'.txt'
    l_file = open(l_file_name, "w", 0777)
    return l_file


###### MAIN START HERE #################
num_args = len(sys.argv)
if num_args < 2:  # the program name and the arguments
  # stop the program and print an error message
  sys.exit("Must provide at least one file to parse")

for i in range(1,num_args):
     print sys.argv[i]

for i in range(1,num_args):
    parse_log_file_for_discovering_protocol_entities(sys.argv[i])

for i in range(1,num_args):
    parse_log_file(sys.argv[i])

generate_sorted_message_list()
generate_sorted_entity_display_list()

g_page_index    = 0
g_message_index = 0
g_message_index_in_current_page = 0
g_now = datetime.datetime.now()
g_now_formated = g_now.strftime("%Y-%m-%d_%H.%M.%S")
g_currentdir = os.curdir
g_resultdir = os.path.join(g_currentdir, g_now_formated)
os.mkdir(g_resultdir, 0777)
os.chdir(g_resultdir)

g_base_file_name = 'mih_mscgen_page_'

g_file = get_nem_file_descriptor()
msc_chart_write_header(g_file)

for message in g_messages:

    if 'msg' in message:
        print ('message %s' % message)
        g_file.write("  %s%s%s [ label = \"(%d) T%s %s\", linecolour=%s , textcolour=%s ] ;\n" % (message['entity_src'], message['arc'], message['entity_dst'], message['seq_no'], message['display_time'], message['msg'], message['line_color'], message['text_color']))
    elif 'box' in message:
        g_file.write("  %s %s %s [ label = \"%s\", textbgcolour=%s , textcolour=%s ] ;\n" % (message['entity_src'], message['box_type'], message['entity_dst'], message['box'], message['textbg_color'], message['text_color']))

    g_message_index                  = g_message_index + 1
    g_message_index_in_current_page  = g_message_index_in_current_page + 1

    if (g_message_index_in_current_page == MAX_MESSAGES_PER_PAGE):
        msc_chart_write_footer(g_file)
        g_file.close()
        msc_chart_generate(g_file.name)
        g_page_index = g_page_index + 1
        g_message_index_in_current_page = 0

        g_file = get_nem_file_descriptor()
        msc_chart_write_header(g_file)


msc_chart_write_footer(g_file)
g_file.close()
if g_message_index_in_current_page >= 1:
    msc_chart_generate(g_file.name)
else:
    print("Removing empty seq diagram file:  %s " % (g_file.name))
    os.remove(g_file.name)
