Python3 Windows平臺PING測試並把結果畫圖方便對比分析


'''
2019-03-21

程序運行說明
測試部分:通過windows自身ping程序運行60秒獲取結果,保存爲本地文本文件(日誌文件)
畫圖部分:處理保存在本地的日誌文件,畫成圖顯示

使用說明
運行平臺:Windows7 & Python3.x
依賴第三方包:matplotlib 包(畫圖要用到)
安裝第三方包:
打開 CMD 輸入命令 pip install matplotlib 會自動安裝 matplotlib 包

BUG:
0、雙擊運行無效,請使用python IDLE打開,再F5運行
1、測試運行後,如果直接關閉GUI窗口,可能造成後臺進程不能終止。請點擊停止按鈕,等待程序關閉後,再關閉窗口
2、用戶輸入的參數沒有限制類型、範圍,不要亂輸入
'''

import tkinter
import pickle
import re
import subprocess
from threading import Thread
import time
import os
import time
import logging # 日誌
Log = logging.getLogger("__name__") # 獲取實例
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') # 指定logger輸出格式
file_handler = logging.FileHandler("net.log") # 日誌文件路徑
file_handler.setFormatter(formatter)  # 可以通過setFormatter指定輸出格式
Log.addHandler(file_handler) # 爲logger添加的日誌處理器
Log.setLevel(logging.DEBUG) # 設置記錄的日誌級別

## 程序運行控制的全局字典變量:'S'控制ping程序運行,'測試按鈕計數'控制按鈕行爲
D_S = {'S':1,'測試按鈕計數':1}
#print(D_S)

## 開始測試,把測試結果保存到日誌文件(開始測試按鈕操作函數)
def PING_TEST():
    #print(D_S)
    if D_S['測試按鈕計數'] %2 != 0:
        D_S['測試按鈕計數'] += 1
        ##print("START 開始測試...")
        text3.insert(1.0, 'START 開始測試...\n')
        D_S['S'] = 1               # 設置爲1 允許測試程序運行
        K1 = 輸入框2.get()         # 獲取輸入框的內容
        V1 = 輸入框1.get()
        K2 = 輸入框4.get()
        V2 = 輸入框3.get()
        K3 = 輸入框6.get()
        V3 = 輸入框5.get()
        K4 = 輸入框8.get()
        V4 = 輸入框7.get()
        D_任務 = {}                # 準備進行PING測試的任務字典 {'備註':'IP'}
        if V1:
            if K1:
                D_任務[K1] = V1
            else:
                K1 = V1
                D_任務[K1] = V1
        if V2:
            if K2:
                D_任務[K2] = V2
            else:
                K2 = V2
                D_任務[K2] = V2
        if V3:
            if K3:
                D_任務[K3] = V3
            else:
                K3 = V3
                D_任務[K3] = V3
        if V4:
            if K4:
                D_任務[K4] = V4
            else:
                K4 = V4
                D_任務[K4] = V4
    
        #print("準備進行PING測試的任務字典",D_任務)
        text1.delete(1.0, tkinter.END)          # 清除文本框內容
        if len(D_任務) == 0:
            text1.insert(1.0, '請先輸入要測試的IP地址')   # 寫入內容
            D_S['S'] = 1
            D_S['測試按鈕計數'] = 1
        else:
            for i in D_任務:
                text1.insert(1.0, '測試任務 ' + i + ' ' + D_任務[i] + '\n')
            text1.insert(5.0, '測試數據保存在 net.log 文件中\n')
            text1.insert(6.0, '測試程序運行中...\n')

        f = open('PING_TEST_INFO.pkl', 'wb')    # 保存到文件,讓後續畫圖程序讀取
        pickle.dump(D_任務, f)
        f.close()
    
        def ping(IP):
            while D_S['S']:
                ##print("線程開始",IP)
                ###text3.insert(tkinter.END, '線程開始 '+IP+'\n')
                ###time.sleep(5)
                
                #print("程序開始", time.time())
                cmd = 'ping -t ' + IP                           # 長PING測試
                win = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
                #print(IP, "子進程PID", win.pid)
        
                #print("程序暫停", time.time())
                time.sleep(60)                                  # 每60秒終止長PING進程,win會計算本次測試結果
                #print("暫停結束", time.time())
        
                #print("開始KILL", time.time())
                os.popen('taskkill /pid:' + str(win.pid))   # windows 需要用自身的命令程序殺死進程
                #print("完成KILL", time.time())
        
                R = win.stdout.read()
                #print("程序完成",time.time())
        
                #print("輸出內容")
                #print(R.decode('GBK'))
        
                TXT = R.decode('GBK')
        
                丟包率 = re.search('[0-9]+%',TXT)
                延時統計 = re.search('最短(.*)',TXT)
        
                #print("丟包率",丟包率.group())
                #print("延時統計",延時統計.group())

                if not 延時統計:
                    if 丟包率:
                        INFO = IP + ' ' + 丟包率.group() + ' 最短 = 0ms,最長 = 0ms,平均 = 0ms'
                        Log.info(INFO) # 記錄到日誌
                        print('100% lost')
                    else:
                        print('ERROR',TXT)
                else:
                    INFO = IP + ' ' + 丟包率.group() + ' ' + 延時統計.group()    # 保存到INFO,準備記錄到日誌
                    Log.info(INFO) # 記錄到日誌
                    #print(INFO)
                
                if D_S['S'] == 1:
                    ##print(IP," 測試中\n")
                    text3.insert(tkinter.END, IP + ' 測試中\n')
                else:
                    ##print(IP," 測試終止\n")
                    text3.insert(tkinter.END, IP + ' 測試終止\n')
                
    
        任務IP列表 = []
        for i in D_任務:
            ##print(D_任務[i])
            任務IP列表.append(D_任務[i])
        任務總數 = len(任務IP列表)
        if 任務總數 == 1:
            t0=Thread(target=ping,args=(任務IP列表[0],))
            t0.start()
        elif 任務總數 == 2:
            t0=Thread(target=ping,args=(任務IP列表[0],))
            t1=Thread(target=ping,args=(任務IP列表[1],))
            t0.start()
            t1.start()
        elif 任務總數 == 3:
            t0=Thread(target=ping,args=(任務IP列表[0],))
            t1=Thread(target=ping,args=(任務IP列表[1],))
            t2=Thread(target=ping,args=(任務IP列表[2],))
            t0.start()
            t1.start()
            t2.start()
        elif 任務總數 == 4:
            t0=Thread(target=ping,args=(任務IP列表[0],))
            t1=Thread(target=ping,args=(任務IP列表[1],))
            t2=Thread(target=ping,args=(任務IP列表[2],))
            t3=Thread(target=ping,args=(任務IP列表[3],))
            t0.start()
            t1.start()
            t2.start()
            t3.start()
        else:
            ##print("ERROR 任務總數範圍不在 1到4")
            text3.insert(tkinter.END, 'ERROR 任務總數範圍不在 1到4 \n')
    else:
        D_S['測試按鈕計數'] += 1
        ##print("STOP 停止中...")
        text3.insert(tkinter.END, 'STOP 停止中... \n')
        D_S['S'] = 0
        text1.delete(1.0, tkinter.END)
        text1.insert(1.0, '測試數據保存在 net.log 文件中\n')
        text1.insert(2.0, '測試程序 終止\n')
        '''
        cmd_find_ping = os.popen('tasklist | findstr -i PING') ## 通過Windows自身命令找到PING進程信息
        cmd_txt = cmd_find_ping.read()
        PING_INFO_LIST = re.findall('PING(.*)',cmd_txt) ## 用re處理成列表,方便接下來使用
        print("找到數量",len(PING_INFO_LIST))
        #print("內容",PING_INFO_LIST)
        for i in PING_INFO_LIST:
            #print("每行內容",i)
            print("進程ID",i.split()[1],"開始結束進程")
            os.popen('taskkill.exe /F /pid:' + str(i.split()[1]))
        '''

## 把測試記錄畫成圖查看(畫圖分析按鈕操作函數)
def INFO_IMG():
    D_複選 = {'PLR':0, 'MAX':0, 'AVG':0}
    text2.delete(1.0, tkinter.END)
    if V1.get() == 1:
        D_複選['PLR'] = 1
        text2.insert(1.0, '在圖中顯示 丟包率\n')
    if V2.get() == 1:
        D_複選['MAX'] = 1
        text2.insert(2.0, '在圖中顯示 最大延時\n')
    if V3.get() == 1:
        D_複選['AVG'] = 1
        text2.insert(3.0, '在圖中顯示 平均延時\n')
    if V1.get() + V2.get() + V3.get() == 0:
        print("用戶無選擇,無Y軸可畫")
        text2.insert(1.0, '【警告】Y軸無內容,請勾選顯示內容:丟包率/最大延時/平均延時\n')
    #print("D_複選 設置結果", D_複選)
    
    X軸刻度設置 = 輸入框9.get()
    if not X軸刻度設置:
        X軸密度降低倍數 = 1
    else:
        X軸密度降低倍數 = int(X軸刻度設置)

    text2.insert(4.0, 'X軸刻度設置:每' + str(X軸密度降低倍數) + '分鐘一個刻度\n')
    
    單選結果 = V.get()
    if 單選結果 == 0:
        text2.insert(5.0, '子圖排列方式:並排顯示子圖\n')
    else:
        text2.insert(5.0, '子圖排列方式:並列顯示子圖\n')
    #print("子圖排列方式:單選結果",V.get())

    ## 分離各IP數據
    D_各IP信息 = {}                             # {'GW':[[TIME],[PLR],[MAX],[AVG]]}
    def IPs(F,IP,TXT):
        IP_INFO = ''
        re_txt = '(.*)' + IP + '(.*)'
        X = re.finditer(re_txt, TXT)
        L_Time = [] # 時間列表
        for i in X:
            T = i.group()
            IP_INFO += T+'\n'
            L_Time.append(T[11:16])             # 元素類型字符串 '時間點'
        ##print(F,"TEXT done")
        ##print(F,"TIME done")
        #print("L_Time \n",L_Time,"\n")
        #print(IP_INFO)
    
        L_PLR = []  # 丟包率
        L_MAX = []  # 最大延時
        L_AVG = []  # 平均延時
        MS = re.findall('[0-9]+ms',IP_INFO)
        PLR = re.findall('[0-9]+%',IP_INFO)
    
        for x in PLR:
            L_PLR.append(int(x[:-1]))           # 取%前的值,再轉成int,保存到列表
        ##print(F,"PLR done")
        
        for y in range(1,len(MS),3):
            L_MAX.append(int(MS[y][:-2]))       # 去掉末尾ms再轉int
        ##print(F,"MAX done")
        #print("L_MAX \n",L_MAX,"\n")
    
        for z in range(2,len(MS),3):
            L_AVG.append(int(MS[z][:-2]))
        ##print(F,"AVG done")
        #print("L_AVG \n",L_AVG,"\n")

        LLLL = [L_Time, L_PLR, L_MAX, L_AVG]
        D_各IP信息[F] = LLLL

    ## 讀取測試記錄文件
    F_Src = 'net.log'
    f = open(F_Src, 'r')
    TXT = f.read()                              # 全部內容保存到內存變量TXT
    #print(TXT,'\n')
    f.close()

    ## 從文件讀取要畫圖的信息
    f = open('PING_TEST_INFO.pkl', 'rb')
    D = pickle.load(f)                          # D = {'GW':'192.168.1.1', 'DNS':'114.114.114.114', 'USA':'8.8.8.8'}
    ##print("本次畫圖內容",type(D), D)
    f.close()

    for i in D:
        text2.insert(6.0, '開始畫圖:' + i + ' ' + D[i] + '\n')
        IPs(i, D[i], TXT)                       # 傳入新文件名,要分離的IP數據,整個源文件內容

    import matplotlib.pyplot as plt
    import matplotlib.ticker as ticker

    ## 爲了作圖方便,把字典的key單獨存爲一個列表
    L = []          # L = ['GW','DNS','USA']
    for i in D:
        L.append(i)

    # 畫出圖形
    圖數量 =  len(L)
    if 圖數量 > 1:
        if 單選結果 == 0:
            fig, ax = plt.subplots(1,圖數量, sharex=True, sharey=True)    # 1行多列(並排顯示子圖),統一X和Y軸刻度,方便橫向對比
        else:
            fig, ax = plt.subplots(圖數量,1, sharex=True, sharey=True)    # 多行1列(並列顯示子圖),統一X和Y軸刻度,方便縱向對比
        AX_List = ax.ravel()                                              # 子圖列表:AX_List[0]第一個子圖,AX_List[1]第二個子圖...

        for i in range(0, 圖數量):
            L_L = D_各IP信息[L[i]]             # L_L內容 = [時間列表,丟包率列表,最大延時列表,平均延時列表]
            L_TIME = L_L[0]     # 時間作爲X軸
            L_PLR = L_L[1]      # 丟包率作爲其中一個Y軸,單位%(百分比)
            L_MAX = L_L[2]      # 最大延時爲其中一個Y軸,單位(ms)
            L_AVG = L_L[3]      # 平均延時爲其中一個Y軸,單位(ms)

            if D_複選['PLR'] == 1:
                AX_List[i].plot(L_TIME,L_PLR,label='PLR(%)')    # 在圖中畫出丟包率(範圍0-100)
            if D_複選['MAX'] == 1:
                AX_List[i].plot(L_TIME,L_MAX,label='MAX(ms)')   # 在圖中畫出最大延時,可以看波動幅度,但遇到太大的會看不清其他值
            if D_複選['AVG'] == 1:
                AX_List[i].plot(L_TIME,L_AVG,label='AVG(ms)')   # 在圖中畫出平均延時
            AX_List[i].xaxis.set_major_locator(ticker.MultipleLocator(X軸密度降低倍數))  # 當X軸取值太多時會顯示太密集,這裏設置每30個值顯示1個
            AX_List[i].legend()                         # 顯示圖例label
            AX_List[i].set_title(L[i])                  # 設置每個子圖各自標題
            if 單選結果 == 0:                           # 如果並排顯示
                AX_List[i].set_xlabel('Time')           # 每個圖顯示X軸標識
            #AX_List[i].set_ylabel('MS or %')           # 設置Y軸標識
        if 單選結果 == 1:                               # 如果並列顯示
            plt.xlabel('Time')                          # 只讓最後一張圖顯示X軸標識
        plt.show()  # 顯示製作好的圖

    elif 圖數量 == 1:
        L_L = D_各IP信息[L[0]]
        L_TIME = L_L[0]
        L_PLR = L_L[1]
        L_MAX = L_L[2]
        L_AVG = L_L[3]
        fig, ax = plt.subplots(1,1)
        if D_複選['PLR'] == 1:
            ax.plot(L_TIME,L_PLR,label='PLR(%)')
        if D_複選['MAX'] == 1:
            ax.plot(L_TIME,L_MAX,label='MAX(ms)')
        if D_複選['AVG'] == 1:
            ax.plot(L_TIME,L_AVG,label='AVG(ms)')
        ax.xaxis.set_major_locator(ticker.MultipleLocator(X軸密度降低倍數))
        plt.legend()
        plt.xlabel('Time')
        plt.title(L[0])
        plt.show()
    else:
        ##print("圖數量 ERROR")
        text3.insert(tkinter.END, '圖數量 ERROR \n')


## GUI 界面設置
top = tkinter.Tk()
top.wm_title("Windows PING")    # 圖形窗口標題
top.geometry("720x650+50+60")   # 圖像窗口初始大小及位置

## 框架佈局
frame_root = tkinter.Frame(top)

frame_1 = tkinter.Frame(frame_root)
frame_2 = tkinter.Frame(frame_root)
frame_3 = tkinter.Frame(frame_root)

frame_4 = tkinter.Frame(frame_root)
frame_5 = tkinter.Frame(frame_root)
frame_6 = tkinter.Frame(frame_root)

frame_7 = tkinter.Frame(frame_root)

## 測試部分的用戶參數處理
測試 = tkinter.Label(frame_1,text="第一步,測試網絡").grid(row=0,column=0,columnspan=4,sticky='W')

備註1 = tkinter.Label(frame_2,text="IP1:").grid(row=1,column=0,sticky='W')
備註2 = tkinter.Label(frame_2,text="備註").grid(row=1,column=2,sticky='W')
備註3 = tkinter.Label(frame_2,text="IP2:").grid(row=2,column=0,sticky='W')
備註4 = tkinter.Label(frame_2,text="備註").grid(row=2,column=2,sticky='W')
備註5 = tkinter.Label(frame_2,text="IP3:").grid(row=3,column=0,sticky='W')
備註6 = tkinter.Label(frame_2,text="備註").grid(row=3,column=2,sticky='W')
備註7 = tkinter.Label(frame_2,text="IP4:").grid(row=4,column=0,sticky='W')
備註8 = tkinter.Label(frame_2,text="備註").grid(row=4,column=2,sticky='W')

輸入框1 = tkinter.Entry(frame_2)
輸入框2 = tkinter.Entry(frame_2)
輸入框3 = tkinter.Entry(frame_2)
輸入框4 = tkinter.Entry(frame_2)
輸入框5 = tkinter.Entry(frame_2)
輸入框6 = tkinter.Entry(frame_2)
輸入框7 = tkinter.Entry(frame_2)
輸入框8 = tkinter.Entry(frame_2)
輸入框1.grid(row=1,column=1,sticky='W')
輸入框2.grid(row=1,column=3,sticky='W')
輸入框3.grid(row=2,column=1,sticky='W')
輸入框4.grid(row=2,column=3,sticky='W')
輸入框5.grid(row=3,column=1,sticky='W')
輸入框6.grid(row=3,column=3,sticky='W')
輸入框7.grid(row=4,column=1,sticky='W')
輸入框8.grid(row=4,column=3,sticky='W')

測試按鈕 = tkinter.Button(frame_3,text='開始測試/結束測試',command=PING_TEST).grid(row=5,column=0,columnspan=4,sticky='W')
text1 = tkinter.Text(frame_3,width='50',height=9)
text1.grid(row=6,column=0,columnspan=4,sticky='W')

## 畫圖部分的用戶參數處理
畫圖部分 = tkinter.Label(frame_4,text="第二步,畫圖分析結果").grid(row=15,column=0,sticky='W')

tkinter.Label(frame_5,text="Y軸顯示內容選擇     ").grid(row=0,column=0,sticky='W')
tkinter.Label(frame_5,text="多圖排列方式選擇    ").grid(row=0,column=1,sticky='W')
tkinter.Label(frame_5,text="X軸刻度設置").grid(row=0,column=2,columnspan=3,sticky='W')
V1 = tkinter.IntVar()
V2 = tkinter.IntVar()
V3 = tkinter.IntVar()
複選1 = tkinter.Checkbutton(frame_5,text='顯示丟包率', variable=V1)
複選1.select() # 默認選中
複選1.grid(row=1,column=0,sticky='W')
複選2 = tkinter.Checkbutton(frame_5,text='顯示最大延時', variable=V2)
複選2.select() # 默認選中
複選2.grid(row=2,column=0,sticky='W')
複選3 = tkinter.Checkbutton(frame_5,text='顯示平均延時', variable=V3)
複選3.select() # 默認選中
複選3.grid(row=3,column=0,sticky='W')

tkinter.Label(frame_5,text="每",width=2).grid(row=1,column=2,sticky='W')
輸入框9 = tkinter.Entry(frame_5,width=3)
輸入框9.grid(row=1,column=3,sticky='W')
tkinter.Label(frame_5,text="分鐘一個刻度").grid(row=1,column=4,sticky='W')
tkinter.Label(frame_5,text="默認值:1").grid(row=2,column=2,columnspan=3,sticky='W')

V = tkinter.IntVar()
## value=0 默認選中
單選1 = tkinter.Radiobutton(frame_5,text="並排顯示", value=0, variable=V).grid(row=1, column=1, sticky='W')
單選2 = tkinter.Radiobutton(frame_5,text="並列顯示", value=1, variable=V).grid(row=2, column=1, sticky='W')

畫圖按鈕 = tkinter.Button(frame_6,text='畫圖分析',command=INFO_IMG).grid(row=0,column=0,sticky='W')
text2 = tkinter.Text(frame_6,width='50',height=9)
text2.grid(row=1,column=0,sticky='W')

text3 = tkinter.Text(frame_7,width='50',height=42)
text3.grid(row=0,column=0,sticky='W')

## 框架的位置佈局
frame_1.grid(row=0,column=0,sticky='W')
frame_2.grid(row=1,column=0,sticky='W')
frame_3.grid(row=2,column=0,sticky='W')
frame_4.grid(row=3,column=0,sticky='W')
frame_5.grid(row=4,column=0,sticky='W')
frame_6.grid(row=5,column=0,sticky='W')
frame_7.grid(row=0,column=1,rowspan=6,sticky='WN')
frame_root.grid(row=0,column=0,sticky='W')

top.mainloop()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章