Demo entry 6632700

python

   

Submitted by anonymous on Jul 28, 2017 at 09:19
Language: Python 3. Code size: 94.9 kB.

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import os
import re
import sys
import time
import yaml
import random
import socket
import signal
import psutil
import pkgutil
import logging
import inspect
import json
import traceback
import threading
import dateutil
import subprocess
import importlib
import tarfile
import lib.constantValues as cv
import tkinter.ttk as ttk
from PIL import ImageTk, Image
try:
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    from matplotlib.figure import Figure
    from matplotlib import pyplot
    import matplotlib.animation as animation
    backend_tkagg = True
except:
    backend_tkagg = False
from collections import deque
try:
    import tkinter as tk # Python 3.x
    import tkinter.scrolledtext as ScrolledText
except ImportError:
    import Tkinter as tk # Python 2.x
    import ScrolledText
from serial.tools.list_ports import comports


__version__ = '1.0'
__author__ = 'zheng.ke@whaley.cn'


case_process = None
all_subprocess = []
running = False


pwd = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(os.path.dirname(__file__), r'testcase'))
sys.path.append(os.path.join(os.path.dirname(__file__), r'usercase'))
sys.path.append(os.path.join(os.path.dirname(__file__), r'lib'))
os.chdir(os.path.join(os.path.abspath(__file__), r'..'))

_schedule_setting={'loop_interval': 15, 'scene': 0, 'external_loop': 1}
_loop_interval = '5Sec'
env_config_file = ''
env_setting = {}

def donothing():
    tp = tk.Toplevel(root)
    x = tp.winfo_x()
    y = tp.winfo_y()
    tp.geometry("+%d+%d"%(x+600, y+400))
    tp.grab_set()
    tp.focus()
    lb = tk.Label(tp, text="V1.0\r\nAny questions, feel free to call Zheng.Ke@whaley.cn")
    lb.grid(padx=20, pady=20)

def kill_children(process):
    children = process.children()#recursive=True
    for e in children:
        kill_children(e)
    logging.debug('stop case pid: %d'%process.pid)
    try:
        process.kill()
    except:
        pass

def send_event2server(case_name, exit_code, plt, doc, time_consume, image_info):
    s = socket.socket()
    s.settimeout(5)
    try:
        s.connect(('172.16.102.134', 50900))
        event = {'case_name': case_name,
                  'exit_code': exit_code,
                  'platform': plt,
                  'image': image_info,
                   'duration': time_consume,
                    'description': re.sub(r'^\s*\r?\n', '', doc, re.M)}
        msg = 'head@whaley.cn' + json.dumps(event) + 'tail@whaley.cn'
        s.send(msg.encode('utf-8'))
        s.recv(1)
        s.close()
    except Exception as msg:
        print (traceback.format_exc())
        logging.debug('Send result to server fail.')

def sig_handler(signum=signal.SIGINT, frame=None):
    global all_subprocess
    if signum in [signal.SIGINT, signal.SIGTERM]:
        for p in all_subprocess:
            kill_children(psutil.Process(p.pid))
        sys.exit(0)

class TextHandler(logging.Handler):
    # This class allows you to log to a Tkinter Text or ScrolledText widget
    # Adapted from Moshe Kaplan: https://gist.github.com/moshekaplan/c425f861de7bbf28ef06

    def __init__(self, text):
        # run the regular Handler __init__
        logging.Handler.__init__(self)
        # Store a reference to the Text it will log to
        self.text = text
        self.p_debug = re.compile(r'\sDEBUG\s+#')
        self.p_error = re.compile(r'\sERROR\s+#')
        self.p_warn = re.compile(r'\sWARNING\s+#')
    def emit(self, record):
        msg = self.format(record)
        def append():
            self.text.configure(state='normal')
            if re.findall(self.p_debug, msg):
                self.text.insert(tk.END, msg + '\n', 'DEBUG')
            elif re.findall(self.p_error, msg):
                self.text.insert(tk.END, msg + '\n', 'ERROR')
            elif re.findall(self.p_warn, msg):
                self.text.insert(tk.END, msg + '\n', 'WARN')
            else:
                self.text.insert(tk.END, msg + '\n')
            self.text.configure(state='disabled')
            self.text.yview(tk.END)

        # This is necessary because we can't modify the Text from other threads
        self.text.after(0, append)

def insert(tree, parent, index, iid=None, **kw):
    """ same method as for standard treeview but add the tag 'unchecked'
        automatically if no tag among ('checked', 'unchecked', 'tristate')
        is given """
    if not "tags" in kw:
        kw["tags"] = ("unchecked",)
    elif not ("unchecked" in kw["tags"] or "checked" in kw["tags"]
              or "tristate" in kw["tags"]):
        kw["tags"] = ("unchecked",)
    ttk.Treeview.insert(self, parent, index, iid, **kw)

class StdoutRedirector(object):
    def __init__(self,text_widget):
        self.text_space = text_widget

    def write(self,string):
        self.text_space.insert('end', string)
        self.text_space.see('end')

class CheckboxTreeview(ttk.Treeview):
    """
        Treeview widget with checkboxes left of each item.
        The checkboxes are done via the image attribute of the item, so to keep
        the checkbox, you cannot add an image to the item.
    """
    def __init__(self, master=None, **kw):
        ttk.Treeview.__init__(self, master, **kw)
        self.im_checked = tk.PhotoImage(file='checked.png')
        self.im_unchecked = tk.PhotoImage(file='unchecked.png')
        self.im_tristate = tk.PhotoImage(file='tristate.png')
        self.tag_configure("unchecked", image=self.im_unchecked)
        self.tag_configure("tristate", image=self.im_tristate)
        self.tag_configure("checked", image=self.im_checked)
        self.tag_configure('fail', background='red')
        #self.tag_configure('head', font=('', 10, "bold"))
        self.bind("<Button-1>", self.box_click, True)

    def insert(self, parent, index, iid=None, **kw):
        """ same method as for standard treeview but add the tag 'unchecked'
            automatically if no tag among ('checked', 'unchecked', 'tristate')
            is given """
        if not "tags" in kw:
            kw["tags"] = ("unchecked",)
        elif not ("unchecked" in kw["tags"] or "checked" in kw["tags"]
                  or "tristate" in kw["tags"]):
            kw["tags"] = ("unchecked",)
        ttk.Treeview.insert(self, parent, index, iid, **kw)

    def check_descendant(self, item):
        """ check the boxes of item's descendants """
        children = self.get_children(item)
        for iid in children:
            self.item(iid, tags=("checked",))
            self.check_descendant(iid)

    def check_ancestor(self, item):
        """ check the box of item and change the state of the boxes of item's
            ancestors accordingly """
        self.item(item, tags=("checked",))
        parent = self.parent(item)
        if parent:
            children = self.get_children(parent)
            b = ["checked" in self.item(c, "tags") for c in children]
            if False in b:
                # at least one box is not checked and item's box is checked
                self.tristate_parent(parent)
            else:
                # all boxes of the children are checked
                self.check_ancestor(parent)

    def tristate_parent(self, item):
        """ put the box of item in tristate and change the state of the boxes of
            item's ancestors accordingly """
        self.item(item, tags=("tristate",))
        parent = self.parent(item)
        if parent:
            self.tristate_parent(parent)

    def uncheck_descendant(self, item):
        """ uncheck the boxes of item's descendant """
        children = self.get_children(item)
        for iid in children:
            self.item(iid, tags=("unchecked",))
            self.uncheck_descendant(iid)

    def uncheck_ancestor(self, item):
        """ uncheck the box of item and change the state of the boxes of item's
            ancestors accordingly """
        self.item(item, tags=("unchecked",))
        parent = self.parent(item)
        if parent:
            children = self.get_children(parent)
            b = ["unchecked" in self.item(c, "tags") for c in children]
            if False in b:
                # at least one box is checked and item's box is unchecked
                self.tristate_parent(parent)
            else:
                # no box is checked
                self.uncheck_ancestor(parent)

    def box_click(self, event):
        """ check or uncheck box when clicked """
        x, y, widget = event.x, event.y, event.widget
        elem = widget.identify("element", x, y)
        if "image" in elem:
            # a box was clicked
            item = self.identify_row(y)
            tags = self.item(item, "tags")
            if ("unchecked" in tags) or ("tristate" in tags):
                self.check_ancestor(item)
                self.check_descendant(item)
            else:
                self.uncheck_descendant(item)
                self.uncheck_ancestor(item)

    def unbindbox(self):
        self.unbind("<Button-1>")

    def bindbox(self):
        self.bind("<Button-1>", self.box_click, True)

class Pkg_upgrade(object):
    def __init__(self):
        self.server_path = r'\\172.16.102.134\AutomationLTS'
        self.tp = tk.Toplevel(root)
        self.tp.geometry("+%d+%d"%(self.tp.winfo_x()+600, self.tp.winfo_y()+400))
        self.tp.grab_set()
        self.tp.focus()
        self.result = False
        if self.compare_change_id():
            self.lb = tk.Label(self.tp, text="Current version is the latest.", fg='blue')
            self.lb.grid(row=0, column=0, padx=20, pady=20)
        else:
            self.st = ScrolledText.ScrolledText(self.tp, width = 120, height=25)
            self.st.insert(tk.END, self.read_release_note())
            self.st.configure(state='disabled')
            self.st.yview(0)
            self.st.grid(row=0, column=0, padx=20, pady=20)        
            self.start_button = tk.Button(self.tp, text='start upgrade', command=self.start)
            self.start_button.grid(row=1, column=0, pady=10)

    @classmethod
    def compare_change_id(cls):
        with open(r"\\172.16.102.134\AutomationLTS\Change-Id") as fin:
            buf = fin.read()
        server_change_id = re.findall(r'Change-Id: (.*)$', buf)
        if not server_change_id:
            return True
        if not os.path.exists('Change-Id'):
            return False
        with open('Change-Id') as fin2:
            buf2 = fin2.read()
        if server_change_id[0] in buf2:
            return True
        else:
            return False

    def read_release_note(self):
        with open(r"\\172.16.102.134\AutomationLTS\release_note.txt") as fin:
            return fin.read()
    
    def start(self):
        self.tp.destroy()
        self.tmp_tp = tk.Toplevel(root)
        self.tmp_tp.geometry("+%d+%d"%(self.tmp_tp.winfo_x()+600, self.tmp_tp.winfo_y()+400))
        self.tmp_tp.grab_set()
        self.tmp_tp.focus()
        self.tmp_tp.columnconfigure(0, weight=1)
        tmp_lb = tk.Label(self.tmp_tp, text='--')
        tmp_lb.grid(padx=20, pady=10)
        td= threading.Thread(target=self._upgrade)
        td.daemon = True
        td.start()
        wait_td = threading.Thread(target=self.waiting, args=(tmp_lb,td))
        wait_td.daemon = True
        wait_td.start()
        
    def waiting(self, tmp_lb, td):
        for _ in range(30):
            tmp_lb.config(text='\\')
            time.sleep(0.2)
            tmp_lb.config(text=r'|')
            time.sleep(0.2)            
            tmp_lb.config(text=r'/')
            time.sleep(0.2) 
            tmp_lb.config(text=r'--')
            time.sleep(0.2)
            if not td.isAlive():
                break
        if self.result:
            tmp_lb.config(text='Successfully')
            tmp_bt = tk.Button(self.tmp_tp, text='Restart', command=self.restart_program)
            tmp_bt.grid(row=1, column=0, pady=10)        
        else:
            tmp_lb.config(text='Upgrade fail.\nTry again or retrieve the package manually.\n%s'%self.server_path)
    
    def _upgrade(self):
        try:
            file_list = os.listdir(r"\\172.16.102.134\AutomationLTS")
            for f in file_list:
                if re.findall(r'AutomationLTS_V\d+\.\d+\.\d+\.tar', f):
                    pkg_name = f
                    break
            tar = tarfile.open(r"\\172.16.102.134\AutomationLTS\%s"%pkg_name)
            tar.extractall(path=r'..\\')
            tar.close()
            self.result = True
        except:
            self.result = False


    def restart_program(self):
        """Restarts the current program.
        Note: this function does not return. Any cleanup action (like
        saving data) must be done before calling this function."""
        python = sys.executable
        os.execl(python, python, * sys.argv)        


def stop_case():
    global all_subprocess
    global case_process
    global running
    running = False
    logging.info('stop running case...')
    if case_process:
        case_process.stdin.write('0')
        case_process.stdin.flush()
        try:
            case_process.wait(timeout=2)
        except subprocess.TimeoutExpired:
            try:
                kill_children(psutil.Process(case_process.pid))
            except Exception as msg:
                pass
        case_process = None
    else:
        logging.debug('No running case.')
    for p in all_subprocess:
        try:
            kill_children(psutil.Process(p.pid))
        except Exception as msg:
            pass


def ask_again():
    tp = tk.Toplevel(root)
    tp.title('EXIT')
    tp.resizable(False, False)
    tp.update_idletasks()
    x = tp.winfo_x()
    y = tp.winfo_y()
    tp.geometry("+%d+%d"%(x+300, y+300))
    tp.grab_set()
    tp.focus()
    l = tk.Label(tp,text="Do you really want to exit?")
    l.grid(row = 0, pady=10)
    ok_bt = tk.Button(tp, text='EXIT', command=lambda: clean_up(tp))
    ok_bt.grid(row = 1, pady=(0, 10))
    tp.iconbitmap(r'whaley1.ico')


def clean_up():
    if running:
        logging.warn('Stop running before close the terminal.')
        return
    #root.destroy()#This can not stop TCL interpreter
    root.quit()


class NewCase(object):
    '''
    New running case instance.
    '''
    def __init__(self, casename, port, plt, logwin, userdefine=False, scene=0, timeout=0, lib_featrue=False):
        global env_config_file
        self.userdefine = userdefine
        self.st = logwin.st
        self.plt = plt
        self.casename = casename
        self.port = port
        self.env_setting = env_config_file
        self.child = None
        self.td_read_stdout = None
        self.td_read_stderr = None
        self.logpath = ''
        self.image_info = ''
        self.scene = scene
        self.timeout = timeout
        self.lib_featrue = lib_featrue

    def start(self):
        global case_process
        if not self.lib_featrue:
            if self.userdefine:
                self.child = subprocess.Popen(['python', r'usercase\%s.py'%self.casename, self.port,
                    self.plt, str(cv.SERIAL_STREAM_SOCKET), str(cv.GRAPHIC_DATA_SOCKET), '--env=%s'%self.env_setting, '--scene=%d'%self.scene],
                    shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, encoding='gb2312',errors='replace')
            else:
                self.child = subprocess.Popen(['python', r'testcase\%s.py'%self.casename, self.port,
                    self.plt, str(cv.SERIAL_STREAM_SOCKET), str(cv.GRAPHIC_DATA_SOCKET), '--env=%s'%self.env_setting, '--scene=%d'%self.scene],
                    shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, encoding='gb2312',errors='replace')
        else:
            self.child = subprocess.Popen(['python', r'lib\%s.py'%self.casename, self.port,
                self.plt, str(cv.SERIAL_STREAM_SOCKET), str(cv.GRAPHIC_DATA_SOCKET), '--env=%s'%self.env_setting, '--scene=%d'%self.scene],
                shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, encoding='gb2312',errors='replace')
        self.td_read_stdout = threading.Thread(target=self.read_log, args=(self.child.stdout, ))
        self.td_read_stdout.daemon = True
        self.td_read_stdout.start()
        self.td_read_stderr = threading.Thread(target=self.read_log, args=(self.child.stderr, ))
        self.td_read_stderr.daemon = True
        #self.td_read_stderr.start()
        case_process = self.child
        self.start_time = time.time()
        if self.timeout > 0 and not self.scene:
            self.case_monitor_td = threading.Thread(target=self._case_monitor)
            self.case_monitor_td.daemon =True
            self.case_monitor_td.start()

    def _case_monitor(self):
        timeout = max(self.timeout*1.2, 600)
        while self.child:
            if time.time() - self.start_time < timeout:
                time.sleep(3)
            else:
                logging.debug('Run case timeout [%dS], force killing...'%self.timeout)
                self.child.stdin.write('1')
                self.child.stdin.flush()
                try:
                    self.child.wait(timeout=2)
                except subprocess.TimeoutExpired:
                    try:
                        kill_children(psutil.Process(self.child.pid))
                    except Exception as msg:
                        pass
                    logging.warn('Run case timeout, force killed.')
                sys.exit()


    def join(self):
        global case_process
        self.child.wait()
        logging.debug('Case process terminated.')
        seconds = time.time() - self.start_time
        self.time_consume = "%.2d:%.2d:%.2d"%(seconds/3600,seconds%3600/60,seconds%3600%60)
        #self.td_read_stdout.join()
        #logging.debug('log read thread exit.')
        #self.td_read_stderr.join()
        #logging.debug('log err read thread exit.')
        case_process = None
        ret = self.child.returncode
        self.child = None
        return ret

    def insert_log(self, p, msg, tag=''):
        self.st.configure(state='normal')
        self.st.insert(p, msg, tag)
        self.st.configure(state='disabled')
        self.st.yview(tk.END)


    def read_log(self, pipe):
        p_debug = re.compile(r'\sDEBUG\s+#')
        p_info = re.compile(r'\sINFO\s+#')
        p_error = re.compile(r'\sERROR\s+#')
        p_warn = re.compile(r'\sWARNING\s+#')
        p_uitools = re.compile(r'\sUITOOLS\s+#')
        p_traceback = re.compile(r'ERROR   # Traceback')
        p_logformat = re.compile(r'\sINFO\s+#|\sWARNING\s+#|\sDEBUG\s+#|\sERROR\s+#')
        p_convert = re.compile('\s[A-Z]+\s+#\s(.*?)[\r\n]')
        p_image = re.compile(r'\[auto detect\] image: (.*)')
        s_traceback = ''
        
        line = pipe.readline()
        if 'Traceback' in line:
            s_traceback = True
            #logging.error(re.findall(r'(.*?)[\r\n]', line)[0])
            self.insert_log(tk.END, line, 'ERROR')
        else:
            ret = re.findall(r'Log: (.*)', line)
            if ret:
                self.logpath = ret[0]
            #logging.info(re.findall(p_convert, line)[0])
            self.insert_log(tk.END, line)
        while True:
            line = pipe.readline()
            if not line:
                break
            ret = re.findall(p_image, line)
            if ret and not self.image_info:
                self.image_info = ret[0]
            if not self.logpath:
                try:
                    self.logpath = re.findall(r'Log: (.*)', line)[0]
                except:
                    pass
            if s_traceback:
                if not re.findall(p_logformat, line):
                    #logging.error(line)
                    self.insert_log(tk.END, line, 'ERROR')
                    continue
                else:
                    s_traceback = False
            if re.findall(p_warn, line):
                logging.warn(re.findall(p_convert, line)[0])
            elif re.findall(p_debug, line):
                logging.debug(re.findall(p_convert, line)[0])
            elif re.findall(p_uitools, line):
                self.insert_log(tk.END, line, 'UITOOLS')
            elif re.findall(p_error, line):
                logging.error(re.findall(p_convert, line)[0])
                if re.findall(p_traceback, line):
                    s_traceback = True
            elif re.findall(p_info, line):
                logging.info(re.findall(p_convert, line)[0])
            else:
                self.insert_log(tk.END, line)
                #logging.info(re.findall(p_convert, line)[0])


def show_code(filename,is_userdefine):
    if is_userdefine:
        subprocess.call(r'python -m idlelib "usercase\%s"'%filename, shell=True)
    else:
        subprocess.call(r'python -m idlelib "testcase\%s"'%filename, shell=True)


class EasyTftp(object):
    '''
    Easy tftp.
    '''
    def __init__(self, port, plt):
        global env_config_file
        self.child = None
        self.port = port
        self.plt = plt
        self.env_setting = env_config_file

    def start(self):
        global all_subprocess
        self.child = subprocess.Popen(['python', r'lib\easy_tftp.py', self.port, self.plt, self.env_setting])
        all_subprocess.append(self.child)

    def join(self):
        global all_subprocess
        self.child.wait()
        all_subprocess.remove(self.child)
        return self.child.returncode

class CaseTree(tk.Frame):
    '''
    Case tree window.
    '''
    def __init__(self, root, *args, **kwargs):
        tk.Frame.__init__(self, root, *args, **kwargs)
        self.tree = None
        self.loop_entry = None
        self.grid(row=1, column=0,rowspan=2,sticky='snwe')
        self.config_frame()
        self.add_treeview()
        self.last_test_result = {'PASS':{}, 'FAIL':{}, 'UNKNOWN':{}}
        self.test_case_dic = {}


    def init_test_result_record(self):
        self.last_test_result = {'PASS':{}, 'FAIL':{}, 'UNKNOWN':{}}

    def config_frame(self):
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

    def get_case_timeout(self, case_name):
        obj = self.test_case_dic.get(case_name, None)
        return getattr(obj.__init__, 'timeout', 0) if obj else 0
    
    def get_case_doc(self,case_name):
        obj = self.test_case_dic.get(case_name, None)
        _doc = getattr(obj, '__doc__', '')
        return _doc if _doc else ''

    def add_treeview(self):
        self.tree = CheckboxTreeview(self)
        self.tree.grid(column=0, row=0, sticky='wsne', columnspan=4)
        self.tree["columns"]=("thr","one","two")
        self.tree.column("#0", width=200 )
        self.tree.column("one", width=30)
        self.tree.column("two", width=40)
        self.tree.column("thr", width=40, anchor='center')
        self.tree.heading("#0", text="Test Case")
        self.tree.heading("one", text="Result")
        self.tree.heading("two", text="Time")
        self.tree.heading("thr", text="Loop")
        self.tree.insert("", 0, "0", text="User-defined")
        self.tree.insert("", 1, "1", text="Platform")
        #self.import_usercase()
        self.tree.bind("<Double-1>", self.OnDoubleClick)

    def OnDoubleClick(self, event):
        #x, y, widget = event.x, event.y, event.widget
        #elem = widget.identify("element", x, y)
        if self.tree.identify_column(event.x) == "#0":
            self.show_source_code(event)
        elif self.tree.identify_column(event.x) == "#1":
            self.set_case_loop(event)
        else:
            pass

    def set_case_loop(self, event):
        global running
        if running:
            return
        if self.loop_entry:
            self.loop_entry.destroy()
        self.loop_entry = None
        rowid = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)
        x,y,width,height = self.tree.bbox(rowid, column)
        pady = height // 2
        self.loop_entry = tk.Entry(self.tree)
        self.loop_entry.place( x=x, y=y+pady, width=self.tree.column("thr")['width'] ,anchor='w')
        self.loop_entry.bind("<Escape>", lambda *ignore: self.loop_entry.destroy())
        self.loop_entry.bind("<Return>", self.save_loop)
        self.loop_entry.focus()

    def save_loop(self, event):
        try:
            loop = int(self.loop_entry.get())
        except ValueError:
            logging.error('Please enter a number!')
            return
        self.tree.item(self.tree.focus(), values=(loop,))
        self.loop_entry.destroy()
        self.loop_entry = None

    def get_test_result(self, case_name, flag=''):
        if not flag:
            fail_result = self.last_test_result['FAIL'].get(case_name, [])
            pass_result = self.last_test_result['PASS'].get(case_name, [])
            unknow_result = self.last_test_result['UNKNOWN'].get(case_name, [])
            return fail_result+pass_result+unknow_result
        elif flag in ['FAIL', 'PASS', 'UNKNOWN']:
            return self.last_test_result[flag].get(case_name, [])
        else:
            logging.error('Unknow flag for test result, flag: %s'%flag)




    #only fail result
    #def save_last_result(self, last_loop):
    def save_last_result(self, result_tuple, flag='UNKNOWN'):
        if flag == 'PASS':
            try:
                self.last_test_result['PASS'][result_tuple[0]] += [(result_tuple[1], result_tuple[2])]
            except KeyError:
                self.last_test_result['PASS'][result_tuple[0]] = [(result_tuple[1], result_tuple[2])]

        elif flag == 'FAIL':
            try:
                self.last_test_result['FAIL'][result_tuple[0]] += [(result_tuple[1], result_tuple[2])]
            except KeyError:
                self.last_test_result['FAIL'][result_tuple[0]] = [(result_tuple[1], result_tuple[2])]

        else:
            try:
                self.last_test_result['UNKNOWN'][result_tuple[0]] += [(result_tuple[1], result_tuple[2])]
            except KeyError:
                self.last_test_result['UNKNOWN'][result_tuple[0]] = [(result_tuple[1], result_tuple[2])]

    def show_source_code(self,event):
        I = self.tree.focus()
        is_userdefine = True if self.tree.parent(I)=='0' else False
        filename = self.tree.item(I)['text'].split('>')[1].strip() + r'.py'
        show_code_td = threading.Thread(target=show_code, args=(filename,is_userdefine))
        show_code_td.daemon = True
        show_code_td.start()

    def get_all_children(tree, item=""):
        children = tree.get_children(item)
        for child in children:
            children += get_all_children(tree, child)
        return children

    def selected_case_name(self):
        I = self.tree.focus()
        if I:
            return self.tree.item(I)['text'].split('>')[1].strip()

    def import_testcase(self, plt, tag=''):
        try:
            self.tree.delete(1)
        except:
            pass
        self.test_case_dic = {}
        case_module_list = [name for _, name, _ in pkgutil.iter_modules(['testcase']) if name != 'base']
        for e in case_module_list:
            try:
                case_module = importlib.import_module(e)
            except Exception as msg:
                logging.error("Load case [%s] fail, %s"%(e, format(msg)))
                continue
            case_module = importlib.reload(case_module)
            for name, obj in inspect.getmembers(case_module):
                if inspect.isclass(obj) and name.startswith('Test'):
                    try:
                        if cv.PLATFORMS in obj.__init__.spt_plt or plt in obj.__init__.spt_plt:              
                            self.test_case_dic[e] = obj
                    except AttributeError as msg:
                        logging.error(format(msg)+ '. Case name: %s'%name)
        self.tree.insert("", 1, "1", text=plt)
        self.tree.item('1', open=True)
        i = 1
        for key in self.test_case_dic.keys():
            self.tree.insert("1", "end","1"+str(i), text= '%d > %s'%(i,key),values=(1,))
            i += 1
        #self.tree.bind('<<TreeviewSelect>>', self.selected_case_name)

    def import_usercase(self, plt, tag=''):
        try:
            self.tree.delete(0)
        except:
            pass
        case_class_dic = {}
        case_module_list = [name for _, name, _ in pkgutil.iter_modules(['usercase']) if name != '__init__']
        for e in case_module_list:
            try:
                case_module = importlib.import_module(e)
            except Exception as msg:
                logging.error("Load case [%s] fail, %s"%(e, format(msg)))
                continue
            case_module = importlib.reload(case_module)
            for name, obj in inspect.getmembers(case_module):
                if inspect.isclass(obj) and name.startswith('Test'):
                    try:
                        if obj.__init__.spt_plt == cv.PLATFORMS or plt in obj.__init__.spt_plt:
                            case_class_dic[e] = obj
                    except AttributeError as msg:
                        logging.error(format(msg)+ '. Case name: %s'%name)
        self.tree.insert("", 0, "0", text="User-defined")
        self.tree.item('0', open=True)
        j = 1
        for key in case_class_dic.keys():
            self.tree.insert("0", "end", "0"+str(j), text= '%d > %s'%(j,key), values=(1,))
            j += 1

    def case_tag_varify(self):
        selected_cases = []
        children = self.tree.tag_has("checked")
        for e in children:
            if self.tree.get_children(e):
                continue
            case_name = re.findall(r'(Test.*)',self.tree.item(e,"text"))[0]
            selected_cases.append(case_name)
        for name, obj in self.test_case_dic.items():
            if name in selected_cases:
                if obj.__init__.pdu:
                    logging.warn('Test case [%s] rely on pdu, please make sure it is set correctly in env setting.'%name)
                if getattr(obj.__init__, 'usb', False):
                    logging.warn("Test case [%s] need USB disk, please make sure there's usb pluged in with necessary files in it.")

    def item_up(self):
        i = self.tree.focus()
        if not i:
            return
        self.tree.move(i, self.tree.parent(i), self.tree.index(i)-1)

    def item_down(self):
        i = self.tree.focus()
        if not i:
            return
        self.tree.move(i, self.tree.parent(i), self.tree.index(i)+1)



class LogWindow(tk.Frame):
    '''
    Main log window, contains three tags.
    LTS log window.
    serial log windw.
    canvas figure monitor window.
    
    LTS log display trace from GUI, case process stdout & stderr.
    serial log window display raw serial trace from miniterm via local socket connection
    canvas window recieve data info from case process via local process,
    and drawn the figure with the data recieved.
    
    '''
    def __init__(self, root, tool_bar, *args, **kwargs):
        tk.Frame.__init__(self, root, *args, **kwargs)
        self.st = None
        self.tool_bar = tool_bar
        self.current_grid = None
        self.grid(column=1,row=1, sticky='wens')
        self.config_frame()
        self.build_st()
        self.config_log()
        self.hook_config()

    def hook_config(self):
        self.tool_bar.clean_log = self.clean_log

    def clean_log(self):
        self.st.config(state='normal')
        self.st.delete(1.0, tk.END)
        self.st.config(state='disable')
        self.serial_st.config(state='normal')
        self.serial_st.delete(1.0, tk.END)
        self.serial_st.config(state='disable')

    def config_frame(self):
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)

    def build_st(self):
        self.button_bar = tk.Frame(self, bg='white')
        self.button_bar.grid(row=0, column=0, sticky='new')
        self.lts_log_bt = tk.Button(self.button_bar, text='LTS', command=self.show_lts_log, width=4)
        self.lts_log_bt.grid(row=0, column=0, sticky='w')
        self.serial_log_bt = tk.Button(self.button_bar, text='Serial', command=self.show_serial_log, width=4)
        self.serial_log_bt.grid(row=0, column=1, sticky='wens')

        self.st = ScrolledText.ScrolledText(self, state='disable', width = 60, height=25)
        self.st.configure(font='TkFixedFont')
        self.st.grid(column=0,row=1, sticky='wens')
        self.current_grid = self.st
        self.st.tag_config('ERROR', foreground='red')
        self.st.tag_config('DEBUG', foreground='grey')
        self.st.tag_config('WARN', foreground='orange')
        self.st.tag_config('UITOOLS', foreground='purple')

        self.serial_st = ScrolledText.ScrolledText(self, state='disable',width = 60, height=25)
        self.serial_st.configure(font='TkFixedFont')
        self.start_serial_log_stream()

        global backend_tkagg
        if backend_tkagg:
            self.canvas_bt = tk.Button(self.button_bar, text='RES', command=self.show_canvas, width=4)
            self.canvas_bt.grid(row=0, column=2, sticky='wens')
            self.canvas = FigureCanvas(self)
            self.canvas.init_canvas()
            self.canvas.start_data_recieve_daemon()
        else:
            pass


    def show_canvas(self):
        self.canvas.grid(column=0,row=1, columnspan=2, sticky='wesn')
        self.current_grid.grid_forget()
        self.current_grid = self.canvas


    def show_lts_log(self):
        self.current_grid.grid_forget()
        self.st.grid(column=0,row=1, sticky='wens')
        self.current_grid = self.st

    def show_serial_log(self):
        self.current_grid.grid_forget()
        self.serial_st.grid(column=0,row=1, sticky='wens')
        self.current_grid = self.serial_st

    def start_serial_log_stream(self):
        self.serial_td = threading.Thread(target=self.serial_log_stream)
        #self.serial_td = threading.Thread(target=self.serial_stream_pipe)
        self.serial_td.daemon = True
        self.serial_td.start()

    '''
    def serial_stream_pipe(self):
        #p = win32pipe.CreateNamedPipe(r'\\.\pipe\\' + cv.SERIAL_STREAM_PIPE,
        _file = r'\\.\pipe\\' + cv.SERIAL_STREAM_PIPE
        for i in range(10):
            pipe_file = _file + r'.%d'%i
            try:
                p = win32pipe.CreateNamedPipe(pipe_file,
                        win32pipe.PIPE_ACCESS_DUPLEX,
                        win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
                        1, 65536, 65536,300,None)
            except pywintypes.error:
                print ('pipe %s not avaliable, try an other one...'%pipe_file)
                continue

        win32pipe.ConnectNamedPipe(p, None)
        while True:
            ret, data = win32file.ReadFile(p, 2048)
            if ret==0:
                self.serial_st.config(state='normal')
                self.serial_st.insert(tk.END, data)
                self.serial_st.config(state='disable')
                self.serial_st.yview(tk.END)
    '''

    def serial_log_stream(self):
        s = socket.socket()
        for i in range(10):
            try:
                s.bind(('127.0.0.1', cv.SERIAL_STREAM_SOCKET+i))
                #print ('bind to socket: %d'%(cv.SERIAL_STREAM_SOCKET+i))
                cv.SERIAL_STREAM_SOCKET += i
            except:
                continue
        s.listen(5)

        while True:
            try:
                con, _ = s.accept()
                while True:
                    text = con.recv(2048).decode('utf-8', 'replace')
                    if not text:
                        break
                    self.serial_st.config(state='normal')
                    self.serial_st.insert(tk.END, text)
                    self.serial_st.config(state='disable')
                    self.serial_st.yview(tk.END)
            except Exception as msg:
                logging.debug(msg)

    def config_log(self):
        user_format = '%(asctime)s %(levelname)-7s # %(msg)s'
        logging.basicConfig(level=logging.DEBUG, filename='whaley.log',format=user_format)
        loghandler = TextHandler(self.st)
        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)
        loghandler.setFormatter(logging.Formatter(user_format))
        logger.addHandler(loghandler)

class ToolBar(tk.Frame):
    def __init__(self, root, casetree, case_tree_frame, *args, **kwargs):
        tk.Frame.__init__(self, root, *args, **kwargs)
        self.casetree = casetree
        self.case_tree_frame = case_tree_frame
        self.box_value = tk.StringVar()
        self.grid(column=0,row=0,columnspan=2,sticky='nwe')
        self.config_frame()
        self.build_toolbar()

    def config_frame(self):
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(7, weight=1)

    def build_toolbar(self):
        self.photo = tk.PhotoImage(file=r'utils\images\whaley_black.png').subsample(11)

        self.config_photo = tk.PhotoImage(file=r'utils\images\config.png').subsample(11)
        self.config = tk.Button(self, image=self.config_photo, command=self.pop_schedule_setting)
        self.config.grid(row=0, column=0, sticky='snw')

        #self.load_photo = tk.PhotoImage(file=r'utils\images\update.png').subsample(11)
        #self.load = tk.Button(self, image=self.load_photo, command=lambda: self.import_case())
        #self.load.grid(row=0, column=6, sticky='snw', padx=(0,5))
        self.varify_photo = tk.PhotoImage(file=r'utils\images\varify.png').subsample(11)
        self.varify_bt = tk.Button(self, image=self.varify_photo, command=lambda: \
            self.case_tree_frame.case_tag_varify())
        self.varify_bt.grid(row=0, column=4, sticky='snw')
        self.bt1 = tk.Button(self, image=self.photo)
        self.bt1.grid(row=0, column=1, sticky='snw')

        self.up_arrow_photo = tk.PhotoImage(file=r'utils\images\up_arrow.png').subsample(11)
        self.bt2 = tk.Button(self, image=self.up_arrow_photo, command=self.case_tree_frame.item_up)
        self.bt2.grid(row=0, column=3, sticky='snw')
        self.down_arrow_photo = tk.PhotoImage(file=r'utils\images\down_arrow.png').subsample(11)
        self.bt3 = tk.Button(self, image=self.down_arrow_photo, command=self.case_tree_frame.item_down)
        self.bt3.grid(row=0, column=2, sticky='snw')
        self.plt_box = ttk.Combobox(self, textvariable=self.box_value, state='readonly', width=9)
        self.plt_box['values'] = ['Platform', cv.HELIOS, cv.APOLLO, cv.FORMULA, cv.PHOEBUS,
            cv.SPHINX, cv.CRONUS, cv.ATHENA, cv.MONE]
        self.plt_box.current(0)
        self.plt_box.bind("<<ComboboxSelected>>", lambda *ignore: self.import_case())
        self.plt_box.grid(column=5, row=0, padx=(10,0))
        self.clean_log_photo = tk.PhotoImage(file=r'utils\images\clean.png').subsample(11)
        self.clean_log_bt = tk.Button(self, image=self.clean_log_photo, command=lambda: self.clean_log())
        self.clean_log_bt.grid(row=0, column=7, sticky='e', padx=5)

        self.socket_cbx_value = tk.StringVar()
        self.socket_check_bt = ttk.Combobox(self, textvariable=self.socket_cbx_value, state='readonly', width=8)
        self.socket_check_bt['values'] = ['UART', 'TCP/IP']
        self.socket_check_bt.current(0)
        self.socket_check_bt.grid(column=6, row=0, padx=(10,0))
        self.socket_check_bt.bind("<<ComboboxSelected>>", lambda *ignore: self.change_connecttype(self.socket_cbx_value.get()))


    def change_connecttype(self, con_type):
        pass

    def get_plt(self):
        plt = self.box_value.get()
        if plt == 'Platform':
            return ''
        return plt

    def pop_schedule_setting(self):
        schedule_setting = ScheduleSetting()

    def import_case(self):
        self.case_tree_frame.import_usercase(self.box_value.get())
        self.case_tree_frame.import_testcase(self.box_value.get())

    def clean_log(self):
        pass

    def disable_con_box(self):
        self.socket_check_bt.config(state='disable')

    def enable_con_box(self):
        self.socket_check_bt.config(state='normal')

    def disable_plt_box(self):
        self.plt_box.config(state='disable')

    def enable_plt_box(self):
        self.plt_box.config(state='normal')


def show_log_fold(path):
    os.startfile(path)


class LogAnalyze(tk.Toplevel):
    '''
    Log analyze engine.
    '''
    def __init__(self, log_path_list):
        super(LogAnalyze, self).__init__(root)
        self.log_path_list = log_path_list
        self.check_list=[]
        self.title('Log analyze')
        self.grab_set()
        self.focus()
        self.geometry('600x400+400+400')
        #self.geometry('+400+400')
        self.resizable(False, False)
        self.iconbitmap(r'whaley1.ico')
        self.config_frame()
        with open(r'log_analyze\analyse_event_register.yaml') as fin:
            self.analyze_register = yaml.load(fin)
        self.build_gui()


    def config_frame(self):
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
        #self.grid_columnconfigure(2, weight=1)
        #self.grid_columnconfigure(3, weight=1)

    def build_gui(self):
        i = 0
        for k, v in self.analyze_register.items():
            self.grid_rowconfigure(int(i/2), weight=1)
            setattr(self, 'check_button_value_%d'%i, tk.BooleanVar())
            getattr(self, 'check_button_value_%d'%i).set(False)
            setattr(self, 'check_button_%d'%i,
                ttk.Checkbutton(self, text=k, variable=getattr(self, 'check_button_value_%d'%i), onvalue=True))
            getattr(self, 'check_button_%d'%i).grid(row=int(i/2), column=int(i%2), sticky='w', padx=(40,0))
            self.check_list.append((k, getattr(self, 'check_button_value_%d'%i)))
            i += 1

        self.grid_rowconfigure(int((i+1)/2), weight=0)
        self.buttom_frame = tk.Frame(self)
        self.buttom_frame.grid(row=int((i+1)/2), column=0, columnspan=2, sticky='ew')

        self.info_label = tk.Label(self.buttom_frame, text=' ')
        self.info_label.grid(row=0, column=0, columnspan=2, pady=5)
        self.external_path_label = tk.Label(self.buttom_frame, text='External log path')
        self.external_path_label.grid(row=1, column=0, sticky='w', padx=(45,0))
        self.external_path_entry = tk.Entry(self.buttom_frame, width=55)
        self.external_path_entry.grid(row=1, column=1, padx=(10,))
        self.go_bt = tk.Button(self.buttom_frame, text='GO', width=6, command=self.start_log_analyze)
        self.go_bt.grid(row=2, column=0, columnspan=2, pady=15)


    def start_log_analyze(self):
        pt = self.external_path_entry.get()
        if pt:
            dir_name = os.path.dirname(pt)
            base_name = os.path.basename(pt)
            ex_log_path = [os.path.join(dir_name, e) for e in os.listdir(dir_name) if re.findall(base_name, e)]
            self.log_path_list += ex_log_path
        if not self.log_path_list:
            self.info_label.config(text='No avaliable log path.', fg='red')
            return
        log_analyze_td = threading.Thread(target=self._log_analyze)
        log_analyze_td.daemon = True
        log_analyze_td.start()
        self.go_bt.config(state='disable')

    def _log_analyze(self):
        global all_subprocess

        for name, check_box in self.check_list:
            if check_box.get():
                try:
                    script_name =  r'log_analyze\\' + self.analyze_register[name]['entry']
                    self.child = subprocess.Popen(['python', script_name] + self.log_path_list, shell=True)
                except (KeyError, Exception) as msg:
                    logging.warn('Analyze %s fail.'%name)
                    logging.warn(msg)
                    continue

                all_subprocess.append(self.child)
                self.child.wait()
                all_subprocess.remove(self.child)
                self.go_bt.config(state='normal')
        self.destroy()
        os.startfile('log_analyze')


class ScheduleSetting(tk.Toplevel):
    '''
    Case running schedule configration wigte.
    constains setting about:
    intervel time
    protect scence switch 
    external loop
    '''
    def __init__(self):
        super(ScheduleSetting, self).__init__(root)
        self.title('Schedule setting')
        self.grab_set()
        self.focus()
        #self.geometry('400x300+300+200')
        self.geometry('+600+400')
        self.resizable(False, False)
        self.iconbitmap(r'whaley1.ico')
        self.config_frame()
        self.build_gui()


    def config_frame(self):
        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)


    def build_gui(self):
        global _loop_interval
        global _schedule_setting

        self.box_value = tk.StringVar()
        self.box = ttk.Combobox(self, textvariable=self.box_value, state='readonly', width=10)
        self.box['values'] = ['5Sec', '15Sec', '30Sec', '1Min', '5Min', '10Min', '30Min']
        if self.box['values']:
            self.box.current(self.box['values'].index(_loop_interval))
        self.box.grid(column=1, row=0, padx=10, pady=10)
        self.loop_interval = tk.Label(self, text='Loop interval')
        self.loop_interval.grid(row=0, column=0, padx=10, pady=10)

        self.scene_label = tk.Label(self, text='Protect scene')
        self.scene_label.grid(row=1, column=0, padx=10, pady=10)
        self.scene_box_value = tk.BooleanVar()
        self.scene_box = ttk.Combobox(self, textvariable=self.scene_box_value, state='readonly', width=10)
        self.scene_box['values'] = ['NO', 'YES']
        self.scene_box.current(int(_schedule_setting.get('scene', 0)))
        self.scene_box.grid(row=1, column=1, padx=10, pady=10)

        self.external_loop_lb = tk.Label(self, text='External loop')
        self.external_loop_lb.grid(row=2, column=0, padx=10, pady=10)
        self.external_loop_ety = tk.Entry(self, width=13)
        self.external_loop_ety.insert(tk.END, _schedule_setting.get('external_loop',1))
        self.external_loop_ety.grid(row=2, column=1, padx=10, pady=10)


        self.ok_frame = tk.Frame(self)
        self.ok_frame.grid(row=3, column=0, sticky='nsew')
        self.ok_frame.grid_columnconfigure(0, weight=1)
        self.ok_frame.grid_rowconfigure(0, weight=1)
        self.ok_bt = tk.Button(self.ok_frame, text='OK', command=self.save_schedule_setting)
        self.ok_bt.grid(row=0, column=1, pady=10, sticky='w')



    def save_schedule_setting(self):
        global _schedule_setting
        global _loop_interval
        _loop_interval = self.box_value.get()
        if _loop_interval == '15Sec':
            _schedule_setting['loop_interval'] = 15
        elif _loop_interval == '30Sec':
            _schedule_setting['loop_interval'] = 30
        elif _loop_interval == '1Min':
            _schedule_setting['loop_interval'] = 60
        elif _loop_interval == '5Min':
            _schedule_setting['loop_interval'] = 300
        elif _loop_interval == '10Min':
            _schedule_setting['loop_interval'] = 600
        elif _loop_interval == '30Min':
            _schedule_setting['loop_interval'] = 1800
        elif _loop_interval == '5Sec':
            _schedule_setting['loop_interval'] = 5
        else:
            logging.error('Unknow schedule interval value: %s'%_loop_interval)
        logging.info('Set loop interval to [%s]'%_loop_interval)
        _schedule_setting['scene'] = 1 if self.scene_box.get()=='YES' else 0
        logging.info('Set protect scene to [%s]'%('YES' if _schedule_setting['scene'] else 'NO'))
        _schedule_setting['external_loop'] = self.external_loop_ety.get()
        logging.info('Set external loop to [%s]'%_schedule_setting['external_loop'])
        self.destroy()



class ShowLog(tk.Toplevel):
    def __init__(self, casetree_frame):
        super(ShowLog, self).__init__(root)
        self.casetree_frame = casetree_frame
        self.case_name = self.casetree_frame.selected_case_name()
        self.fail_loop = casetree_frame.last_test_result['FAIL'].get(self.case_name, [])
        self.pass_loop = casetree_frame.last_test_result['PASS'].get(self.case_name, [])
        self.other_loop = casetree_frame.last_test_result['UNKNOWN'].get(self.case_name, [])
        self.total_loop = 1
        self.current_log_loop = 1
        self.title('Case Log')
        self.grab_set()
        self.focus()
        self.geometry('800x600')
        #self.resizable(False, False)
        self.iconbitmap(r'whaley1.ico')
        self.current_grid = None
        self.head_bar = tk.Frame(self)
        self.tab_button_frame = tk.Frame(self)
        self.config_frame()
        self.build_gui()
        self.show_first_log()

    def config_frame(self):
        self.grid_rowconfigure(2, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.head_bar.grid_rowconfigure(0, weight=1)
        self.head_bar.grid_columnconfigure(0, weight=2)
        self.head_bar.grid_columnconfigure(1, weight=2)
        self.head_bar.grid_columnconfigure(2, weight=1)
        self.head_bar.grid_columnconfigure(3, weight=1)
        self.head_bar.grid_columnconfigure(4, weight=1)
        self.head_bar.grid_columnconfigure(5, weight=2)
        self.head_bar.grid_columnconfigure(6, weight=2)

    def build_gui(self):
        self.head_bar.grid(row=0, column=0, sticky='enw')
        self.tab_button_frame.grid(row=1, column=0, sticky='enw')
        self.box_value = tk.StringVar()
        self.box = ttk.Combobox(self.head_bar, textvariable=self.box_value, state='readonly', width=10)
        self.box.bind("<<ComboboxSelected>>", lambda *ignore: self.show_first_log())
        self.box['values'] = ['PASS', 'FAIL', 'UNKNOWN']
        self.box.current(0)
        self.box.grid(row=0, column=5)
        self.pre_log_bt = tk.Button(self.head_bar, text='<<<', command=self.print_pre_log)
        self.pre_log_bt.grid(row=0, column=2, sticky='w')
        self.next_log_bt = tk.Button(self.head_bar, text='>>>', command=self.print_next_log)
        self.next_log_bt.grid(row=0, column=4, sticky='e')
        self.index_label = tk.Label(self.head_bar, text=r'1/%d'%self.total_loop)
        self.index_label.grid(row=0, column=3, pady=6)
        self.analyze_bt = tk.Button(self.head_bar, text='Analyze', command=self.log_analyze)
        self.analyze_bt.grid(row=0, column=6, sticky='w')
        self.lts_log_st = ScrolledText.ScrolledText(self, state='disabled',width = 150, height=50)
        self.lts_log_st.grid(row=2, column=0, sticky='sewn', padx=5, pady=(0,6))
        self.lts_log_st.tag_config('ERROR', foreground='red')
        self.lts_log_st.tag_config('DEBUG', foreground='grey')
        self.lts_log_st.tag_config('WARN', foreground='orange')
        self.lts_log_st.tag_config('UITOOLS', foreground='purple')
        self.img_label = tk.Label(self, text='text', width = 150, height=50)
        #self.img_label.grid(row=2, column=0, sticky='sewn', padx=5, pady=(0,6))
        self.log_st = ScrolledText.ScrolledText(self, state='disabled',width = 150, height=50)
        self.log_st.grid(row=2, column=0, sticky='sewn', padx=5, pady=(0,6))
        self.current_grid = self.log_st


        self.lts_log_bt = tk.Button(self.tab_button_frame, text='LTS', command=self.show_lts_log, width=5)
        self.lts_log_bt.grid(row=0, column=0)
        self.serial_log_bt = tk.Button(self.tab_button_frame, text='Serial', command=self.show_serial_log, width=5)
        self.serial_log_bt.grid(row=0, column=1)
        self.logcat_log_bt = tk.Button(self.tab_button_frame, text='Logcat', width=5)
        self.logcat_log_bt.grid(row=0, column=2)
        self.photo_log_bt = tk.Button(self.tab_button_frame, text='Screen', command=self.show_photo, width=5)
        self.photo_log_bt.grid(row=0, column=3)

        global backend_tkagg
        if backend_tkagg:
            self.canvas = FigureCanvas(self)
            self.canvas.init_canvas()
            self.resource_bt = tk.Button(self.tab_button_frame, text='Figure', command=self.show_res_canvas, width=5)
            self.resource_bt.grid(row=0, column=4)
        else:
            pass

        self.log_path_label = tk.Label(self.tab_button_frame, text='')
        self.log_path_label.grid(row=0, column=5, padx=10)



    def log_analyze(self):
        loop_type = self.box_value.get()
        if loop_type == 'PASS':
            loop = self.pass_loop
        elif loop_type == 'FAIL':
            loop = self.fail_loop
        elif loop_type == 'UNKNOWN':
            loop = self.other_loop

        log_path_list = []
        for e in loop:
            log_path_list.append(e[1])
        #if not log_path_list:
        #    self.log_path_label.config(text='No avaliable log to analyze.', fg='red')
        #    return
        log_analyze_console = LogAnalyze(log_path_list)


    def open_log_fold(self, path):
        show_fold_td = threading.Thread(target = show_log_fold, args=(path,))
        show_fold_td.daemon = True
        show_fold_td.start()

    def show_res_canvas(self):
        self.current_grid.grid_forget()
        self.canvas.grid(row=2, column=0, sticky='sewn', padx=5, pady=(0,6))
        self.current_grid = self.canvas

    def show_photo(self):
        self.current_grid.grid_forget()
        self.img_label.grid(row=2, column=0, sticky='sewn', padx=5, pady=(0,6))
        self.current_grid = self.img_label

    def show_lts_log(self):
        self.current_grid.grid_forget()
        self.lts_log_st.grid(row=2, column=0, sticky='sewn', padx=5, pady=(0,6))
        self.current_grid = self.lts_log_st

    def show_serial_log(self):
        self.current_grid.grid_forget()
        self.log_st.grid(row=2, column=0, sticky='sewn', padx=5, pady=(0,6))
        self.current_grid = self.log_st

    def print_next_log(self):
        if self.current_log_loop+1 <= self.total_loop:
            self.current_log_loop += 1
            self.print_log(self.current_log_loop)

    def print_pre_log(self):
        if self.current_log_loop-1 > 0:
            self.current_log_loop -= 1
            self.print_log(self.current_log_loop)

    def print_lts_log_thead(self, log_path):
        log_file = log_path + r'\lts.log'
        p_debug = re.compile(r'\sDEBUG\s+#')
        p_error = re.compile(r'\sERROR\s+#')
        p_warn = re.compile(r'\sWARNING\s+#')
        p_uitools = re.compile(r'\sUITOOLS\s+#')
        with open(log_file, 'r') as fin:
            buf = fin.read()
            self.lts_log_st.configure(state='normal')
            for l in buf.splitlines():
                if re.findall(p_debug, l):
                    self.lts_log_st.insert(tk.END, l+'\r\n', 'DEBUG')
                elif re.findall(p_error, l):
                    self.lts_log_st.insert(tk.END, l+'\r\n', 'ERROR')
                elif re.findall(p_uitools, l):
                    self.lts_log_st.insert(tk.END, l+'\r\n', 'UITOOLS')
                elif re.findall(p_warn, l):
                    self.lts_log_st.insert(tk.END, l+'\r\n', 'WARN')
                else:
                    self.lts_log_st.insert(tk.END, l+'\r\n')
            self.lts_log_st.configure(state='disabled')
            self.lts_log_st.yview(tk.END)

    def print_serial_log_thead(self, log_path):
        log_file = log_path + r'\serial.log'
        with open(log_file, 'rb') as fin:
            buf = fin.read()
            self.log_st.configure(state='normal')
            self.log_st.insert(tk.END, buf)
            self.log_st.configure(state='disabled')
            self.log_st.yview(tk.END)

    def print_log(self, loop):
        self.lts_log_st.configure(state='normal')
        self.lts_log_st.delete(1.0, tk.END)
        self.lts_log_st.configure(state='disabled')
        self.log_st.configure(state='normal')
        self.log_st.delete(1.0, tk.END)
        self.log_st.configure(state='disabled')
        global backend_tkagg
        if backend_tkagg:
            self.canvas.init_canvas()

        loop_type = self.box_value.get()
        if loop_type == 'PASS':
            self.total_loop = len(self.pass_loop)
            if self.total_loop == 0:
                self.index_label.config(text='0/0')
                self.log_path_label.config(text='')
                return
            self.index_label.config(text='%d/%d'%(loop, self.total_loop))
            log_path = self.pass_loop[loop-1][1]
        elif loop_type == 'FAIL':
            self.total_loop = len(self.fail_loop)
            if self.total_loop == 0:
                self.index_label.config(text='0/0')
                self.log_path_label.config(text='')
                return
            self.index_label.config(text='%d/%d'%(loop, self.total_loop))
            log_path = self.fail_loop[loop-1][1]
        elif loop_type == 'UNKNOWN':
            self.total_loop = len(self.other_loop)
            if self.total_loop == 0:
                self.index_label.config(text='0/0')
                self.log_path_label.config(text='')
                return
            self.index_label.config(text='%d/%d'%(loop, self.total_loop))
            log_path = self.other_loop[loop-1][1]
        else:
            logging.error('Unknow test result type: %s'%loop_type)
            return

        self.log_path_label.config(text=log_path, fg='black')
        self.log_path_label.bind('<Double-1>', lambda *ignore: self.open_log_fold(log_path))
        print_td = threading.Thread(target=self.print_serial_log_thead, args= (log_path,))
        print_td.daemon = True
        print_td.start()
        print_lts_td = threading.Thread(target=self.print_lts_log_thead, args= (log_path,))
        print_lts_td.daemon = True
        print_lts_td.start()
        if backend_tkagg:
            self.canvas.init_canvas()
            draw_canvas_td = threading.Thread(target=self.canvas.draw_static_data, args= (log_path + r'\canvas_data.txt',))
            draw_canvas_td.daemon = True
            draw_canvas_td.start()


    def show_first_log(self):
        self.current_log_loop = 1
        self.after(200, lambda: self.print_log(self.current_log_loop))

def dump_config_yaml(suffix):
    global env_config_file
    global env_setting
    env_config_file = r'config/env_setting_%s.yaml'%suffix
    with open(env_config_file, 'w') as fin:
        yaml.dump(env_setting, fin, default_flow_style=False)



class EnvSetting(tk.Toplevel):
    def __init__(self):
        super(EnvSetting, self).__init__(root)
        self.pdu_type = ['r16t', 'np1601']
        self.resizable(False, False)
        self.title('Env setting')
        self.center()
        self.pdu = []
        self.pdu_slot = [i for i in range(1,17)]
        self.iconbitmap(r'whaley1.ico')
        self.config_frame()
        self.build_setting_item()
        self.grab_set()
        self.focus()
        self.load_config()


    def load_config(self):
        global env_setting
        global env_config_file
        '''
        try:
            with open(self.env_config_file) as fin:
                _env_setting = yaml.load(fin)
                if not _env_setting:
                    return
                env_setting = _env_setting
        except Exception as msg:
            pass
        '''
        if env_setting.get('pdu_setting', ''):
            self.pdu_type_box.current(self.pdu_type.index(env_setting['pdu_setting']['pdu_type']))
            self.pdu_slot_box.current(self.pdu_slot.index(env_setting['pdu_setting']['pdu_slot']))
            self.pdu_ip_Entry.delete(0, tk.END)
            self.pdu_ip_Entry.insert(tk.END, env_setting['pdu_setting']['pdu_ip'])

        self.logcat_cbox.current(int(env_setting.get('LOGCATCTL', 1))-1)



    def center(self):
        self.update_idletasks()
        x = self.winfo_x()
        y = self.winfo_y()
        self.geometry("+%d+%d"%(x+300,y+200))

    def config_frame(self):
        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_rowconfigure(2, weight=1)
        self.grid_rowconfigure(3, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure(2, weight=1)
        self.grid_columnconfigure(3, weight=1)
        self.grid_columnconfigure(4, weight=1)
        self.grid_columnconfigure(5, weight=1)

    def build_setting_item(self):
        self.info_label = tk.Label(self, text='')
        self.info_label.grid(column=2, row=0)
        #PDU setting
        self.pdu_label = tk.Label(self, text='PDU', width=15)
        self.pdu_label.grid(column=0, row=1)
        self.box_value = tk.StringVar()
        self.pdu_type_box = ttk.Combobox(self, textvariable=self.box_value, width=12, state='readonly')
        self.pdu_type_box['values'] = self.pdu_type
        self.pdu_type_box.grid(column=1, row=1,padx=(0,10))
        self.pdu_ip_Entry = tk.Entry(self, width=15)
        self.pdu_ip_Entry.insert(tk.END, '0.0.0.0')
        self.pdu_ip_Entry.grid(column=2, row=1, padx=10)
        self.box_value = tk.StringVar()
        self.pdu_slot_box = ttk.Combobox(self, textvariable=self.box_value, width=7, state='readonly')
        self.pdu_slot_box['values'] = self.pdu_slot
        self.pdu_slot_box.grid(column=3, row=1, padx=10)
        self.pdu_test_bt = tk.Button(self, text='Test', command=self.test_pdu)
        self.pdu_test_bt.grid(column=4, row=1, padx=10)
        self.pdu_test_result_label = tk.Label(self, text=' ', width=10)
        self.pdu_test_result_label.grid(column=5, row=1, padx=10)

        #WIFI setting
        self.wifi_label = tk.Label(self, text='WIFI', width=15)
        self.wifi_label.grid(row=2, column=0)
        self.wifi_ssid_entry = tk.Entry(self,width=15, fg='grey')
        self.wifi_ssid_entry.insert(tk.END, 'Enter ssid')
        self.wifi_ssid_entry.grid(row=2, column=1, padx=(0,10))
        self.wifi_psk_entry = tk.Entry(self, width=15, fg='grey')
        self.wifi_psk_entry.insert(tk.END, 'Enter password')
        self.wifi_psk_entry.grid(row=2, column=2, padx=10)
        self.wifi_key_mgmt_entry = tk.Entry(self, width=10)
        self.wifi_key_mgmt_entry.insert(tk.END, 'WPA-PSK')
        self.wifi_key_mgmt_entry.config(state='disable')
        self.wifi_key_mgmt_entry.grid(row=2, column=3, padx=10)
        #LOGCAT setting
        self.logcat_store_lb = tk.Label(self, text='LOGCAT', width=15)
        self.logcat_store_lb.grid(column=0, row=3, pady=5)
        self.logcat_var = tk.StringVar()
        self.logcat_cbox = ttk.Combobox(self, textvariable=self.logcat_var, width=12, state='readonly')
        self.logcat_cbox['values'] = ['Auto', 'Manual', 'USB', 'Except-pass']
        self.logcat_cbox.current(0)
        self.logcat_cbox.grid(column=1, row=3, padx=(0,10), pady=5)

        self.save_bt= self.pdu_test_bt = tk.Button(self, text='Save', command=self.save_env_setting)
        self.save_bt.grid(column=2, row=4, pady=10)
        self.pdu_type_box.current(0)
        self.pdu_slot_box.current(0)


    def save_env_setting(self):
        global env_setting
        if not re.findall(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', self.pdu_ip_Entry.get()):
            self.info_label.config(text='Unknow ip address', fg='red')
            return
        env_setting['pdu_setting'] = {'pdu_type': self.pdu_type_box.get(),
             'pdu_ip': self.pdu_ip_Entry.get(), 'pdu_slot': int(self.pdu_slot_box.get())}

        if self.wifi_ssid_entry.get() != 'Enter ssid' and self.wifi_psk_entry.get() != 'Enter password':
            env_setting['wifi_setting'] = {'ssid': self.wifi_ssid_entry.get(), 'psk': self.wifi_psk_entry.get()}

        env_setting['LOGCATCTL'] = self.logcat_cbox['values'].index(self.logcat_cbox.get()) +1
        self.destroy()



        #self.info_label.config(text='')


    def test_pdu(self):
        test_td = threading.Thread(target=self.reboot_pdu)
        test_td.daemon = True
        test_td.start()


    def reboot_pdu(self):
        self.pdu_test_bt.config(state='disabled')
        if self.pdu_type_box.get() == 'r16t':
            import electric_relay
            slot = int(self.pdu_slot_box.get())
            er = electric_relay.ElectricRelay(self.pdu_ip_Entry.get())
            if er.switch_off(slot) and er.switch_on(slot):
                self.pdu_test_result_label.config(text='Pass', fg='blue')
            else:
                self.pdu_test_result_label.config(text='Fail', fg='red')
        elif self.pdu_type_box.get() == 'np1601':
            import powerManager
            slot = int(self.pdu_slot_box.get())
            if powerManager.TvPowerControl.test(self.pdu_ip_Entry.get(), slot, 'np1601'):
                self.pdu_test_result_label.config(text='Pass', fg='blue')
            else:
                self.pdu_test_result_label.config(text='Fail', fg='red')
        self.pdu_test_bt.config(state='normal')



class FuncZone(tk.Frame):
    def __init__(self, root, logwin, casetree, casstree_frame, tool_bar, *args, **kwargs):
        tk.Frame.__init__(self, root, *args, **kwargs)
        self.pdu_setting = []
        self.grid(column=1,row=2, sticky='nwse')
        self.casetree = casetree
        self.tool_bar = tool_bar
        self.casetree_frame = casstree_frame
        self.logwin = logwin
        self.tool_bar = tool_bar
        self.config_frame()
        self.build_zone()
        self.hook_config()

    def hook_config(self):
        self.tool_bar.change_connecttype = self.change_con_type

    def change_con_type(self, con_type):
        if con_type == 'UART':
            self.box.grid(column=0, row=0)
            self.tcpip_entry.grid_forget()
        elif con_type == 'TCP/IP':
            self.tcpip_entry.grid(column=0, row=0, padx=(5,0))
            self.box.grid_forget()
            self.tcpip_entry.delete(0, tk.END)
            self.tcpip_entry.insert(tk.END, '0.0.0.0:0')
        else:
            logging.error('Unknown type connection type: %s'%con_type)

    def get_con_info(self):
        con_type = self.tool_bar.socket_check_bt.get()
        if con_type == 'UART':
            value = self.box.get()
        else:
            value = self.tcpip_entry.get()

        return (con_type, value)

    def config_frame(self):
        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_rowconfigure(2, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure(2, weight=1)

    def build_zone(self):
        self.auto_run_var = tk.BooleanVar()
        self.auto_run_var.set(False)
        self.auto_run = ttk.Checkbutton(self, text="Run case after flash", variable=self.auto_run_var, onvalue=True)
        self.auto_run.grid(column=1, row=0)
        self.add_photo = tk.PhotoImage(file=r'utils\images\addcase.png').subsample(6)
        self.user_case= tk.Button(self, image=self.add_photo,
            command=lambda:self.add_user_define_case(self.casetree, self.tool_bar), bd = 0, bg='white')
        self.user_case.grid(column=0, row=2)
        self.flash_photo = tk.PhotoImage(file=r'utils\images\flash.png').subsample(6)
        self.tftp= tk.Button(self, image=self.flash_photo, command=self.image_flash, bd = 0, bg='white')
        self.tftp.grid(column=1, row=1)
        self.root_photo = tk.PhotoImage(file=r'utils\images\root.png').subsample(6)
        self.su_root = tk.Button(self, image=self.root_photo, command=self.su, bd = 0, bg='white')
        self.su_root.grid(column=2, row=1)
        self.setting_photo = tk.PhotoImage(file=r'utils\images\setting.png').subsample(6)
        self.setting = tk.Button(self, image=self.setting_photo, command=self.env_setting, bd = 0, bg='white')
        self.setting.grid(column=1, row=2)
        self.run_photo = tk.PhotoImage(file=r'utils\images\run.png').subsample(6)
        self.stop_photo = tk.PhotoImage(file=r'utils\images\stop.png').subsample(6)
        self.run_button = tk.Button(self, image=self.run_photo, command=self.call_run_case, bd = 0, bg='white')
        self.run_button.image = self.run_photo
        self.run_button.grid(column=0, row=1)
        self.box_value = tk.StringVar()
        self.box = ttk.Combobox(self, textvariable=self.box_value, state='readonly', width=18,
            postcommand = self.refreshcomport)
        self.box['values'] = tuple(port for port, desc, hwid in comports())
        if self.box['values']:
            self.box.current(0)
        self.box.grid(column=0, row=0)
        self.tcpip_entry = tk.Entry(self, width=20)

        self.analyse_photo = tk.PhotoImage(file=r'utils\images\debug.png').subsample(6)
        self.analyse_bt = tk.Button(self, image=self.analyse_photo, command=self.show_log, bd=0, bg='white')
        self.analyse_bt.grid(column=2, row=2)

    def su(self):
        su_td = threading.Thread(target= self.solo_root)
        su_td.daemon = True
        su_td.start()


    def show_log(self):
        select_case_name = self.casetree_frame.selected_case_name()
        if select_case_name:
            log_st = ShowLog(self.casetree_frame)
        else:
            logging.info('Please select a case.')

    def env_setting(self):
        env_set_window = EnvSetting()

    def input_varify(self):
        port_type, port = self.get_con_info()
        if port_type == 'UART' and port == '':
            logging.error('Start fail. Please connect COM port cable.')
            return False
        elif port_type == 'TCP/IP':
            if r'0.0.0.0'in port or not re.findall('\d+.\d+.\d+.\d+:\d+', port):
                logging.error('Please set an avaliable [ip:port] address.')
                return  False
        if self.tool_bar.plt_box.get() == 'Platform':
            logging.error('Please choice a Platform.')
            return False
        return True


    def lock_operation(self):
        self.tftp.config(state='disabled')
        self.su_root.config(state='disabled')
        self.tcpip_entry.configure(state='disable')
        self.run_button.configure(image=self.stop_photo)
        self.run_button.image = self.stop_photo
        self.tool_bar.disable_plt_box()
        self.tool_bar.disable_con_box()
        self.casetree.unbindbox()
        self.box.config(state='disabled')

    def unlock_operation(self):
        self.run_button.configure(image=self.run_photo)
        self.run_button.image = self.run_photo
        self.tftp.config(state='normal')
        self.su_root.config(state='normal')
        self.tcpip_entry.configure(state='normal')
        self.tool_bar.enable_plt_box()
        self.tool_bar.enable_con_box()
        self.casetree.bindbox()
        self.box.config(state='readonly')

    def call_run_case(self):
        global running
        suffix = '%s_%s'%(self.tool_bar.get_plt(), self.get_con_info()[1].replace(':', '_'))
        dump_config_yaml(suffix)
        if self.run_button.image == self.run_photo:
            if not self.casetree.tag_has("checked"):
                logging.info('Please select at least one case.')
                return
            if not self.input_varify():
                return

            call_td = threading.Thread(target=self.run_case, name='running case monitor thread.')
            call_td.daemon = True
            call_td.start()
            self.lock_operation()
        elif self.run_button.image == self.stop_photo:
            td = threading.Thread(target=stop_case)
            td.daemon= True
            td.start()

    def image_flash(self):
        suffix = '%s_%s'%(self.tool_bar.get_plt(), self.get_con_info()[1].replace(':', '_'))
        dump_config_yaml(suffix)
        self.tftp_td = threading.Thread(target=self.easy_tftp )
        self.tftp_td.daemon =True
        self.tftp_td.start()

    def solo_root(self):
        if not self.input_varify():
            return
        _solo_root = NewCase('solo_root', self.get_con_info()[1],
            self.tool_bar.plt_box.get(), self.logwin, lib_featrue=True)
        _solo_root.start()
        self.lock_operation()
        ret_code = _solo_root.join()
        self.unlock_operation()
        if ret_code == 9:
            logging.info('Root successfully.')
        else:
            logging.error('Root fail.')


    def easy_tftp(self):
        if not self.input_varify():
            return
        tftp = EasyTftp(self.get_con_info()[1], self.tool_bar.plt_box.get())
        tftp.start()
        self.tftp.config(state='disabled')
        self.su_root.config(state='disabled')
        self.run_button.config(state='disabled')
        retcode = tftp.join()
        if retcode == 9:
            logging.info('Burn new image successfully.')
        elif retcode == 3:
            logging.error('Burn new image fail. Please check if com port is in use.')
        elif retcode == 5:
            logging.error('Burn new image fail. Please check if TV is awake.')
        else:
            logging.error('Burn new image fail, return code: %d'%retcode)
        self.tftp.config(state='normal')
        self.su_root.config(state='normal')
        self.run_button.config(state='normal')
        if retcode == 9 and self.auto_run_var.get():
            self.call_run_case()

    def refreshcomport(self):
        self.box['values'] = tuple(port for port, desc, hwid in comports())
        if self.box['values']:
            self.box.current(0)
        else:
            self.box.set('')

    def convert_loop(self, str_loop):
        try:
            loop = int(str_loop)
            return loop
        except:
            loop = re.findall(r'\d+/(\d+)', str_loop)
            if not loop:
                logging.error('Unknow error: get loop fail. %s'%str_loop)
                return False
            return int(loop[0])

    def run_case(self):
        global running
        global _schedule_setting
        running = True
        returncode = -1
        fail_loop = []
        try:
            self.save_loop(None)
        except:
            pass
        #find checked or tristate parent items
        parents = self.casetree.get_children()
        checked_parents = []
        for p in parents:
            if self.casetree.tag_has("tristate", item=p) or self.casetree.tag_has("checked", item=p):
                checked_parents.append(p)
        if not checked_parents:
            return

        con_port = self.get_con_info()[1]
        root.title('WHALEY LTS - %s'%con_port)
        #root.title('WHALEY LTS - 172.16.113.16:4196')
        self.casetree_frame.init_test_result_record()
        #loop every checked or tristate parent item
        for p_item in checked_parents:
            if not running:
                continue
            self.casetree.item(p_item, values=())
            #retrieve checked children under current parent item
            children = self.casetree.get_children(item=p_item)
            checked_children = [child for child in children if self.casetree.tag_has('checked', item=child)]
            external_loop = int(_schedule_setting['external_loop'])

            #external_loop = self.convert_loop(self.casetree.item(p_item)['values'][0])
            #external loop
            for j in range(external_loop):
                if not running:
                    continue
                if external_loop >1:
                    self.casetree.item(p_item, values=("%d/%d"%(j+1, external_loop)))
                #init every child result state
                for e in checked_children:
                    if self.casetree.get_children(e):
                        continue
                    if j >1:
                        #here loop is the times case has done, to get init loop number, it should divide the external loop number
                        loop = int(self.convert_loop(self.casetree.item(e)['values'][0])/j)
                    else:
                        loop = self.convert_loop(self.casetree.item(e)['values'][0])
                    self.casetree.item(e, values=(loop,'Waiting',), tags=('checked'))
                #internal loop
                for e in checked_children:
                    #init waiting child result when manually cancel running
                    if not running:
                        self.casetree.item(e, values=(loop,))
                        continue
                    if self.casetree.get_children(e):
                        continue
                    #start
                    #if j >1:
                        #here loop is the times case has done, to get init loop number, it should divide the external loop number
                    #    loop = self.convert_loop(self.casetree.item(e)['values'][0])/j
                    #else:
                    loop = int(self.casetree.item(e)['values'][0])
                    is_userdefine = True if self.casetree.parent(e)=='0' else False
                    case_name = re.findall(r'(Test.*)',self.casetree.item(e,"text"))[0]
                    start_time = time.time()
                    for i in range(loop):
                        if returncode not in [-1, 9]:
                            wait = _schedule_setting['loop_interval']
                            #print ('wait: {}'.format(wait))
                            while running and wait>0:
                                time.sleep(0.5)
                                wait -= 0.5
                        if not running:
                            break
                        logging.info('--------------------------- New loop %d/%d-----------------------'%(i+1, loop))
                        self.casetree.item(e, values=("%d/%d"%(i+1, loop),'Running',))
                        case = NewCase(case_name, con_port, self.tool_bar.plt_box.get(),
                            self.logwin, userdefine=is_userdefine, scene=_schedule_setting.get('scene', 0),
                            timeout=self.casetree_frame.get_case_timeout(case_name))
                        case.start()
                        returncode = case.join()
                        send_event2server(case_name, returncode, self.tool_bar.plt_box.get(),
                                        self.casetree_frame.get_case_doc(case_name),case.time_consume,
                                        case.image_info)
                        if returncode in cv.ERR_CODE_CASE_SUCCESS_SET:
                            self.casetree_frame.save_last_result((case_name, i+1, case.logpath), 'PASS')
                        elif returncode in cv.ERROR_EXIT_SET:
                            logging.debug('Return code: {}.'.format(returncode))
                            self.casetree_frame.save_last_result((case_name, i+1, case.logpath), 'UNKNOWN')
                            break
                        elif returncode in cv.ERR_CODE_CASE_FAIL_SET:
                            self.casetree_frame.save_last_result((case_name, i+1, case.logpath), 'FAIL')
                            fail_loop.append((case_name, i+1, case.logpath))
                        else:
                            self.casetree_frame.save_last_result((case_name, i+1, case.logpath), 'UNKNOWN')
                        del case
                        time.sleep(0.1)
                        logging.debug('Return code: {}.'.format(returncode))

                    stop_time = time.time()
                    seconds = stop_time - start_time
                    time_consume = "%.2d:%.2d:%.2d"%(seconds/3600,seconds%3600/60,seconds%3600%60)

                    pass_cnt = len(self.casetree_frame.get_test_result(case_name, flag='PASS'))
                    fail_cnt = len(self.casetree_frame.get_test_result(case_name, flag='FAIL'))
                    unknown_cnt = len(self.casetree_frame.get_test_result(case_name, flag='UNKNOWN'))
                    total_cnt = pass_cnt + fail_cnt + unknown_cnt
                    if fail_cnt > 0:
                        self.casetree.item(e, values=("%d/%d"%(pass_cnt, total_cnt), 'Fail', time_consume),
                            tags=('checked','fail'))
                    elif unknown_cnt > 0:
                        self.casetree.item(e, values=("%d/%d"%(pass_cnt, total_cnt), 'Unknown', time_consume))
                    else:
                        self.casetree.item(e, values=("%d/%d"%(pass_cnt, total_cnt), 'Pass', time_consume))

                    logging.info('Case:%s finished.pass: %d, fail: %s, unknow: %d'%
                        (case_name, pass_cnt, fail_cnt, unknown_cnt))
                    logging.info('='*80)
                    if not error_info(returncode):
                        break
            self.casetree.item(p_item, values=())
        logging.info('All cases finished.')

        for n, l, p in fail_loop:
            logging.warn('Fail case: %s, loop: %d, log path: %s'%(n, l, p))
        self.unlock_operation()
        running = False
        root.title('WHALEY LTS')

    def add_user_define_case(self, root_tree, tool_bar):
        new_window = UserCaseDefine(root_tree, tool_bar)

def error_info(returncode):
    if returncode == cv.ERR_CODE_OPEN_UART_FAIL:
        logging.error('Can not open serial port. Please close the app using serial port or re-plug in serial port and try again.')
        return False
    elif returncode == cv.ERR_CODE_CONNECT_NO_RESPONSE:
        logging.error('Serial port no output with echo test.')
        logging.error('Please check if serial cable is connected to TV uart port correctly and TV is awake(not in twice standby or STR mode).')
        return False
    elif returncode == cv.ERR_CODE_PDU_SETTING_ABSENT:
        logging.error('Case need pdu control, please make sure pdu env setting is correct.')
        return False
    return True

class UserCaseDefine(object):
    '''
    User-defined case engine.
    '''
    def __init__(self, root_tree, tool_bar):
        self.root_tree = root_tree
        self.tool_bar = tool_bar
        self.toplevel = tk.Toplevel()
        self.toplevel.resizable(False, False)
        self.toplevel.title('BUILD CASE')
        self.l_tree = None
        self.add_left_tree()
        self.r_tree = None
        self.add_right_tree()
        self.add_button()
        self.add_info_label()
        self.toplevel.grab_set()
        self.toplevel.focus()
        self.top_casename = None
        self.case_name = ''
        self.l_tree.bind('<Double-1>', lambda *ignore: self._select())
        self.r_tree.bind('<Double-1>', lambda *ignore: self._delete())
        self.toplevel.iconbitmap(r'whaley1.ico')

    def add_button(self):
        self.select_button = tk.Button(self.toplevel, text='Select', command=self._select)
        self.delete_button = tk.Button(self.toplevel, text='Delete', command=self._delete)
        self.apply_button = tk.Button(self.toplevel, text='Save', width=5, command=self._save)
        self.up_button = tk.Button(self.toplevel, text='Up', width=5, command=self._item_up)
        self.down_button = tk.Button(self.toplevel, text='Down', width=5, command=self._item_down)
        self.select_button.grid(column=2, row=2,pady=20,padx=20)
        self.delete_button.grid(column=3, row=2,pady=20,padx=20)
        self.up_button.grid(sticky='ne',column=4, row=4,pady=(0,10),padx=10)
        self.down_button.grid(column=5, row=4,pady=(0,10),padx=10)
        self.apply_button.grid(sticky='nw',column=6, row=4,pady=(0,10),padx=10)
        self.apply_button.configure(state='disabled')

    def _item_up(self):
        i = self.r_tree.focus()
        if not i:
            return
        self.r_tree.move(i, self.r_tree.parent(i), self.r_tree.index(i)-1)

    def _item_down(self):
        i = self.r_tree.focus()
        if not i:
            return
        self.r_tree.move(i, self.r_tree.parent(i), self.r_tree.index(i)+1)

    def _save(self):
        self.top_casename=tk.Toplevel(self.toplevel)
        self.top_casename.resizable(False, False)
        self.top_casename.iconbitmap(r'whaley1.ico')
        self.center()
        self.top_casename.grab_set()
        self.l=tk.Label(self.top_casename,text="Enter case name:")
        self.l.grid(row=0)
        self.e=tk.Entry(self.top_casename)
        self.e.grid(row=1)
        self.e.bind("<Return>", lambda *ignore: self.save_new_case())
        self.e.focus()
        self.b=tk.Button(self.top_casename, text='Ok', command=self.save_new_case)
        self.b.grid(row=2)

    def save_new_case(self):
        self.casename=self.e.get()
        if not self.casename:
            logging.error('Please enter case name.')
            return
        self.casename = 'Test_' + self.casename
        self.top_casename.destroy()
        self.top_casename = None
        self.generate_case(self.casename)
        if self.load_case(self.casename):
            for e in self.r_tree.get_children():
                self.r_tree.delete(e)
            self.apply_button.configure(state='disabled')

    def _delete(self):
        c = self.r_tree.focus()
        if c:
            self.r_tree.delete(c)
        if not self.r_tree.get_children():
            self.apply_button.configure(state='disabled')

    def _select(self):
        curItem = self.l_tree.focus()
        l = len(self.r_tree.get_children())
        t = self.l_tree.item(curItem)['text']
        self.r_tree.insert("",l+1, text=t)
        self.apply_button.configure(state='normal')

    def add_info_label(self):
        self.info_label = tk.Label(self.toplevel, text='ABOUT_TEXT', bg='white', height=20, width=30)
        self.info_label.grid(column=2, row=0, columnspan=2, rowspan=2,pady=20,padx=20)


    def generate_case(self, case_name):
        action_list = [case_name, self.tool_bar.box_value.get()]
        for e in self.r_tree.get_children():
            if self.r_tree.get_children(e):
                continue
            action_list.append(self.r_tree.item(e)['text'])

        cmd = r'python lib/CaseGenerator.py'
        for e in action_list:
            cmd += ' '+e
        ret = subprocess.call(cmd, shell=True)
        logging.debug('Case generate return code: %d'%ret)


    def load_case(self,casename):
        case_module = importlib.import_module("usercase.%s"%casename)
        children = self.root_tree.get_children('0')
        for name, obj in inspect.getmembers(case_module):
            if inspect.isclass(obj) and name.startswith('Test'):
                self.root_tree.insert('0','01', text="%d > %s"%(len(children)+1, name),values=(1,))
                logging.info('Load new case successfully.')
                return True
        logging.error('Load new case fail.')
        return False

    def add_left_tree(self):
        self.l_tree = ttk.Treeview(self.toplevel)
        self.l_tree.bind("<ButtonRelease-1>", self.show_action_des)
        self.l_tree.grid(column=0, row=0, sticky='wsne', columnspan=2, rowspan=3,pady=20,padx=20)
        self.l_tree.column("#0", width=203)
        self.l_tree.heading("#0", text="Sub actions")
        with open(r'utils/action_register.yaml') as fin:
            self.actions = yaml.load(fin)
        for e in self.actions.keys():
            self.l_tree.insert("" , list(self.actions.keys()).index(e), text=e)


    def show_action_des(self, event):
        action_name = self.l_tree.item(self.l_tree.focus())['text']
        action_des = self.actions[action_name]['description']
        self.info_label.configure(text=action_des)

    def add_right_tree(self):
        self.r_tree = ttk.Treeview(self.toplevel)
        self.r_tree.grid(column=4, row=0, sticky='wsne', columnspan=3, rowspan=3,pady=20,padx=20)
        self.r_tree.column("#0", width=203)
        self.r_tree.heading("#0", text="User case define")

    def center(self):
        self.toplevel.update_idletasks()
        x = self.toplevel.winfo_x()
        y = self.toplevel.winfo_y()
        self.top_casename.geometry("+%d+%d"%(x+300,y+200))

def build_menu():
    menubar = tk.Menu(root)
    filemenu = tk.Menu(menubar, tearoff=0)
    filemenu.add_command(label="New", command=donothing)
    filemenu.add_command(label="Open", command=donothing)
    filemenu.add_command(label="Save", command=donothing)
    filemenu.add_command(label="Save as...", command=donothing)
    filemenu.add_command(label="Close", command=donothing)
    filemenu.add_separator()
    #filemenu.add_command(label="Exit", command=clean_up)
    filemenu.add_command(label="Exit", command=clean_up)
    menubar.add_cascade(label="File", menu=filemenu)
    helpmenu = tk.Menu(menubar, tearoff=0)
    helpmenu.add_command(label="Upgrade...", command=Pkg_upgrade)
    helpmenu.add_command(label="Help Index", command=donothing)
    helpmenu.add_command(label="About...", command=donothing)
    menubar.add_cascade(label="Help", menu=helpmenu)
    root.config(menu=menubar)
    root.protocol("WM_DELETE_WINDOW", clean_up)
    root.geometry('{}x{}'.format(1400, 700))
    #root.resizable(False, False)
    root.title('WHALEY LTS')
    root.iconbitmap(r'whaley1.ico')
    root.option_add('*tearOff', 'FALSE')
    root.grid_rowconfigure(1, weight=3)
    root.grid_rowconfigure(2, weight=1)
    root.grid_columnconfigure(0, weight=1)
    root.grid_columnconfigure(1, weight=3)


class FigureCanvas(tk.Frame):
    def __init__(self, *a, **ak):
        tk.Frame.__init__(self, *a, **ak)
        self.xdata = deque([], maxlen=800)
        self.xdata2 = deque([], maxlen=800)
        self.ydata = deque([], maxlen=800)
        self.ydata2 = deque([], maxlen=800)
        self.build_axis()
        self.canvas_socket = None

    def start_data_recieve_daemon(self):
        self.socket_td = threading.Thread(target=self.start_canvas_socket)
        self.socket_td.daemon = True
        self.socket_td.start()

    def draw_static_data(self, data_file):
        if not os.path.isfile(data_file):
            return

        with open(data_file) as fin:
            content = fin.read()
        cpu_tuple = re.findall(r'(\d+:\d+:\d+) 1(\d.\d+)', content)
        mem_tuple = re.findall(r'(\d+:\d+:\d+) 2(\d.\d+)', content)

        for x, y in cpu_tuple:
            self.xdata.append(dateutil.parser.parse(x))
            self.ydata.append(int(float(y)*100))
        for x, y in mem_tuple:
            self.xdata2.append(dateutil.parser.parse(x))
            self.ydata2.append(int(float(y)*100))
        if len(cpu_tuple) > 1:
            self.axis1.set_xlim(dateutil.parser.parse(cpu_tuple[0][0]), dateutil.parser.parse(cpu_tuple[-1][0]))
        elif len(mem_tuple) > 1:
            self.axis1.set_xlim(dateutil.parser.parse(mem_tuple[0][0]), dateutil.parser.parse(mem_tuple[-1][0]))
        else:
            return
        self.cpu_line.set_data(self.xdata, self.ydata)
        self.mem_line.set_data(self.xdata2, self.ydata2)
        self.canvas.draw()

    def build_axis(self):
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        #pyplot.locator_params(axis='x', nticks=4)
        self.fig = pyplot.Figure()
        self.axis1 = self.fig.add_subplot(111)
        self.axis1.set_yticks(range(0, 101, 10))
        self.cpu_line, = self.axis1.plot([], [], 'g-', label='cpu ratio', lw=1)

        self.mem_line, = self.axis1.plot([], [], 'm-', label='memory ratio', lw=1)
        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.canvas.show()
        self.canvas.get_tk_widget().grid(row=0, column=0, sticky='ewns')

    def init_canvas(self):
        self.xdata = deque([], maxlen=800)
        self.xdata2 = deque([], maxlen=800)
        self.ydata = deque([], maxlen=800)
        self.ydata2 = deque([], maxlen=800)
        self.axis1.set_yticks(range(0, 101, 10))
        init_t = time.time()
        init_t -= init_t%60
        self.t_start = time.localtime(init_t)
        self.t_end = time.localtime(init_t+600)
        self.axis1.set_xlim(dateutil.parser.parse(time.strftime('%H:%M:00',self.t_start)), dateutil.parser.parse(time.strftime('%H:%M:00', self.t_end)))
        self.canvas.draw()


    def start(self, con):
        self.init_canvas()
        while True:
            info_type = 0
            info_type, ratio = self.recv_data(con)
            if info_type == 0:
                break
            t_now = time.time()
            if t_now > time.mktime(self.t_end):
                self.t_end = time.localtime(t_now + 60)
                self.t_start = time.localtime(t_now -540)
                self.axis1.set_xlim(dateutil.parser.parse(time.strftime('%H:%M:00',self.t_start)), dateutil.parser.parse(time.strftime('%H:%M:00', self.t_end)))
            if info_type == 1:
                self.ydata.append(ratio)
                self.xdata.append(dateutil.parser.parse(time.strftime('%H:%M:%S',time.localtime(t_now))))
                self.cpu_line.set_data(self.xdata, self.ydata)
            elif info_type == 2:
                self.ydata2.append(ratio)
                self.xdata2.append(dateutil.parser.parse(time.strftime('%H:%M:%S',time.localtime(t_now))))
                self.mem_line.set_data(self.xdata2, self.ydata2)
            self.canvas.draw()
        logging.debug('Canvas update thread exit.')
        raise SystemExit(0)

    def recv_data(self, con):
        pattern = re.compile(cv.SOCKET_MAGICKEY_HEAD + r'(\w)(\d\.\d\d)' + cv.SOCKET_MAGICKEY_END)
        data = ''
        while True:
            try:
                b = con.recv(1).decode('utf-8')
            except Exception as msg:
                logging.debug(msg)
                return (0, 0)
            if b == '':
                return (0, 0)
            data += b
            ret = re.findall(pattern, data)
            if ret:
                info_type = int(ret[0][0])
                ratio = float(ret[0][1])*100
                return (info_type, ratio)

    def start_canvas_socket(self):
        self.s = socket.socket()
        for i in range(10):
            try:
                self.s.bind(('127.0.0.1', cv.GRAPHIC_DATA_SOCKET+i))
                cv.GRAPHIC_DATA_SOCKET += i
            except:
                continue
        self.s.listen(5)
        #self.s.settimeout(5)
        new_td = None
        while True:
            try:
                con, _ = self.s.accept()
            except Exception as msg:
                logging.warn(msg)
                continue
            if new_td and new_td.is_alive():
                logging.warn('Pre-canvas thread is still running.')
            new_td = threading.Thread(target=self.start, args=(con,))
            new_td.daemon = True
            new_td.start()


def check_new_version():
    if not Pkg_upgrade.compare_change_id():
        Pkg_upgrade()

if __name__ == '__main__':
    root = tk.Tk()
    #root.resizable(False, False)
    build_menu()
    case_tree = CaseTree(root)
    tool_bar = ToolBar(root, case_tree.tree, case_tree, height=25)
    log_window = LogWindow(root, tool_bar, bg='red', pady=1)
    Func_zone = FuncZone(root, log_window, case_tree.tree, case_tree, tool_bar, bg='white', pady=1)
    root.after(700, lambda: check_new_version())    
    root.mainloop()

This snippet took 0.16 seconds to highlight.

Back to the Entry List or Home.

Delete this entry (admin only).