0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ろうとるがPythonを扱う、、(その33:iperfのRealtimeグラフ表示)

Posted at

iperfの結果をリアルタイム表示

Pythonにて、iperfの結果をリアルタイム表示に表示する。tkinter、subprocess、matplotlibを利用。それぞれ、小生の過去の記事をもとに作成した。

レイアウト(フレーム配置)については、適宜、ChatGPTに相談。なお、Windowsのiperf Version2を利用。Version3を使わなかった背景は下記参照。

なお、本コード(またはEXE)が存在するフォルダーに実体である”iperf2.exe”が存在することが前提である。

結果

まずは結果から記載。

Client

Client.png

Server

Server.png

Bandwidthはすべてに共通であるが、JitterおよびLossはUDP Serverのみに適用される。

ファイル

”Graph Save”をクリックするとグラフが保存される。また、iperfの結果をCSVファイルに保存する。下記は作成されたファイル。

SavedFiles.png

CSVファイルサンプルは下記である。

CSV.png

グラフファイルサンプルは下記。

iperf-server_udp_bandwidth_20250413_091311.png

ソースコード

いつものように、ポイントを記載。

ライブラリ

## Import
import sys
import threading
import subprocess
import logging
import logging.handlers
import time
from datetime import datetime
from socket import inet_aton
import re
from tkinter import *
import tkinter.ttk as ttk
import tkinter.messagebox as messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 
from matplotlib.figure import Figure 

ここは説明略。

変数など

## Version
version = 'Beta'

## List
tab_list = [' Client ', ' Server ']
btn_list = [' Start ', ' Stop ']
log_list = ['iperf-client', 'iperf-server']
proto_list = ['UDP', 'TCP']

## Flag
Doing = [0, 0] 			# Client, Server

## Default Parameters
default_ip = '127.0.0.1'	# Loopback
default_port = 5001		# iperf2 default port
default_time = 10		# 10 sec

## CSV Header (UDP: All,  TCP: until by Bandwidth)
header = ['Date & Time', 'Src IP', 'Src Port', 'Dst IP', 'Dst Port',\
          'ID', 'Interval', 'Transfer(byte)', 'Bandwidth(bps)',\
          'Jitter(ms)', 'Lost', 'Total', 'Loss Rate(%)']

## For plotting
xpos = []
xpos2 = []
xpos3 = []
ypos = []
ypos2 = []
ypos3 = []
  • リスト(*_list):GUI上のボタン、ログファイル、プロトコル
  • Doing:実行中かどうかのフラグ
  • デフォルト値(IPアドレス、ポート番号、実行時間)
  • header:CSVファイルヘッダ
  • *pos*:グラフ位置

tkinterメイン

############### Start of main program ###############
# root main window
root = Tk()
ver_str = 'iperfGraph ' + version
root.title(ver_str)
root.geometry("1300x600")

# Create an instance of ttk style
fgcolor = "lightskyblue2"
bgcolor = "gray80"
style = ttk.Style()
style.theme_create("style1", parent="alt", settings={
    "TNotebook.Tab": {
        "configure": {"background": bgcolor },
        "map":       {"background": [("selected", fgcolor)],
                      } },
    "TMenu": {
        "configure": {"background": "red" },
    } } )
style.theme_use("style1")

# Create Notebook Widget
note = ttk.Notebook(root)

# Create tab
tab0 = Frame(note)	# Client
tab1 = Frame(note)	# Server

# Add tab
note.add(tab0, text=tab_list[0])
note.add(tab1, text=tab_list[1])

# Create tab content
create_content(tab0, 0)	# Client
create_content(tab1, 1)	# Server

# Locate tab
note.pack(expand=True, fill='both')

# Menu
debug = False
tgl = BooleanVar(value = debug)
menubar = Menu()
filemenu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=filemenu)
filemenu.add_command(label="Exit", command=sys.exit)
filemenu.add_checkbutton(label="Debug", command=log_tgl)
root.config(menu=menubar)

# main loop
root.mainloop()

######## End of loop ########
  • Window作成
  • タブ作成
  • メニュー作成

tkinter関連、詳細は略。

フレーム作成

######## Client&Server Tab (create_content) ########
def create_content(frm, arg):
    frm1 = Frame(frm)
    frm2 = Frame(frm)
    frm3 = Frame(frm)
    frm4 = Frame(frm)
    frm5 = Frame(frm)

5つのフレーム構成。

  1. 入力やボタン
  2. iperfの実行結果表示ボックス
  3. Bandwidthグラフ
  4. Jitterグラフ
  5. Lossグラフ
    # Frame 1
    if arg == 0:	# Client
       label1 = Label(frm1, text=' IP address ')
       label1.grid(row=0, column=0)
       frm.entryIP = Entry(frm1, width=16)
       frm.entryIP.grid(row=0, column=1)
       frm.sel_proto = IntVar(value=0)
       rb_udp = Radiobutton(frm1, text=" UDP ", variable=frm.sel_proto, value=0)
       rb_udp.grid(row=0, column=2)
       rb_tcp = Radiobutton(frm1, text=" TCP ", variable=frm.sel_proto, value=1)
       rb_tcp.grid(row=0, column=3)
       label2 = Label(frm1, text=' Port ')
       label2.grid(row=0, column=4)
       frm.entryPort = Entry(frm1, width=6)
       frm.entryPort.grid(row=0, column=5)
       label3 = Label(frm1, text=' Bandwith ')
       label3.grid(row=0, column=6)
       frm.entryBandwidth = Entry(frm1, width=6)
       frm.entryBandwidth.grid(row=0, column=7)
       label4 = Label(frm1, text=' Length ')
       label4.grid(row=0, column=8)
       frm.entryLength = Entry(frm1, width=6)
       frm.entryLength.grid(row=0, column=9)
       label5 = Label(frm1, text=' Time(sec) ')
       label5.grid(row=0, column=10)
       frm.entrySecond = Entry(frm1, width=6)
       frm.entrySecond.grid(row=0, column=11)
       label6 = Label(frm1, text=' ')
       label6.grid(row=0, column=12)
       frm.Btn = Button(frm1, text=btn_list[0], command=lambda:start_clk(frm, arg))
       frm.Btn.grid(row=0, column=13)
       label7 = Label(frm1, text=' ')
       label7.grid(row=0, column=14)
       frm.Btn2 = Button(frm1, text=' Graph Save ', command=lambda:save_clk(frm, arg))
       frm.Btn2.grid(row=0, column=15)
       frm.cmd = Label(frm1, text='\t\t\t\t\t\t\t')
       frm.cmd.grid(row=0, column=16)
    else:		# Server
       frm.sel_proto = IntVar(value=0)
       rb_udp = Radiobutton(frm1, text=" UDP ", variable=frm.sel_proto, value=0)
       rb_udp.grid(row=0, column=0)
       rb_tcp = Radiobutton(frm1, text=" TCP ", variable=frm.sel_proto, value=1)
       rb_tcp.grid(row=0, column=1)
       label1 = Label(frm1, text=' Port ')
       label1.grid(row=0, column=2)
       frm.entryPort = Entry(frm1, width=6)
       frm.entryPort.grid(row=0, column=3)
       label2 = Label(frm1, text=' ')
       label2.grid(row=0, column=4)
       frm.Btn = Button(frm1, text=btn_list[0], command=lambda:start_clk(frm, arg))
       frm.Btn.grid(row=0, column=5)
       label3 = Label(frm1, text=' ')
       label3.grid(row=0, column=6)
       frm.Btn2 = Button(frm1, text=' Graph Save ', command=lambda:save_clk(frm, arg))
       frm.Btn2.grid(row=0, column=7)
       frm.cmd = Label(frm1, text='\t\t\t\t\t\t')
       frm.cmd.grid(row=0, column=8)
    frm1.grid(row=0, column=0, columnspan=2, sticky="n")

フレーム1

  • Client
    • 入力フィールド定義:IPアドレス、UDP/TCP選択、ポート、負荷帯域、Payloadサイズ、実行時間
    • ボタン:Start/Stop、グラフ保存
    • 実行コマンド表示用フィールド
  • Server
    • 入力フィールド定義:UDP/TCP選択、ポート
    • ボタン:Start/Stop、グラフ保存
    • 実行コマンド表示用フィールド
    # Frame 2
    frm.text = Text(frm2, width=70, height=13, font=("",12,'bold'), bg="#ffe", bd=2)
    frm2.ysc = Scrollbar(frm2, orient=VERTICAL, command=frm.text.yview)
    frm.text["yscrollcommand"] = frm2.ysc.set
    frm2.ysc.grid(row=0, column=1, sticky="ns")
    frm.text.config(state='disabled')
    frm.text.grid(row=0, column=0, sticky="nsew")
    frm2.grid(row=1, column=0, sticky="nsew")

フレーム2

  • 実行結果表示テキストボックス
  • 縦スクロールバー
    # Frame 3
    frm.fig = Figure() 
    frm.ax = frm.fig.add_subplot(111) 
    frm.ax.set_xlabel("Time (s)") 
    frm.ax.set_ylabel("Bandwidth (Mbps)")
    frm.fig.tight_layout()  		# レイアウト調整
    frm.fig.subplots_adjust(bottom=0.2)	# 下側に余白を確保
    frm.fig.subplots_adjust(left=0.15)
    frm.graph = FigureCanvasTkAgg(frm.fig, master=frm3)
    frm.graph.get_tk_widget().grid(row=0, column=0, sticky="nsew")
    frm3.grid(row=1, column=1, sticky="nsew")

    # Frame 4
    frm.fig2 = Figure() 
    frm.ax2 = frm.fig2.add_subplot(111) 
    frm.ax2.set_xlabel("Time (s)") 
    frm.ax2.set_ylabel("Jitter (ms)") 
    frm.fig2.tight_layout()
    frm.fig2.subplots_adjust(bottom=0.4)
    frm.fig2.subplots_adjust(left=0.15)
    frm.graph2 = FigureCanvasTkAgg(frm.fig2, master=frm4)
    frm.graph2.get_tk_widget().grid(row=0, column=0, sticky="nsew")
    frm4.grid(row=2, column=0, sticky="nsew")

    # Frame 5
    frm.fig3 = Figure() 
    frm.ax3 = frm.fig3.add_subplot(111) 
    frm.ax3.set_xlabel("Time (s)") 
    frm.ax3.set_ylabel("Loss (%)") 
    frm.fig3.tight_layout()
    frm.fig3.subplots_adjust(bottom=0.4)
    frm.fig3.subplots_adjust(left=0.15)
    frm.graph3 = FigureCanvasTkAgg(frm.fig3, master=frm5)
    frm.graph3.get_tk_widget().grid(row=0, column=0, sticky="nsew")
    frm5.grid(row=2, column=1, sticky="nsew")

フレーム3、4、5

  • matplotlibによるグラフ作成準備
    # グリッドの調整(リサイズ対応)
    frm.grid_columnconfigure(0, weight=1)
    frm.grid_columnconfigure(1, weight=1)
    frm.grid_rowconfigure(1, weight=1)
    frm.grid_rowconfigure(2, weight=1)

    frm3.grid_rowconfigure(0, weight=1)
    frm4.grid_rowconfigure(0, weight=1)
    frm5.grid_rowconfigure(0, weight=1)

    return
  • フレーム配置の微調整

デバッグ

######## For Debug Log ########
def log_tgl():
    global debug
    tgl.set(not tgl.get())
    if tgl.get():
        debug = True
    else:
        debug = False
    return
  • メニュー”Debug”が選択された時の処理(フラグの反転、ログ用)

入力チェック関数

######## Check IP Address Format (is_valid_ip) ########
def is_valid_ip(addr):
    try:
        inet_aton(addr)
        return True
    except:
        return False

######## Bandwidth Parameter Check (is_valid_bandwidth) ########
def is_valid_bandwidth(arg):
    if arg.count('.') <= 1:	# Only 1 decimal point
        m = re.match(r'[0-9][0-9.]*[kKmMgG]', arg)
        if m == None:
            return False
        else:
            return True
    else:
        return False
  • is_valid_ip():入力されたIPアドレスの正当性チェック
  • is_valid_bandwidth():入力された帯域の正当性チェック(iperf version2仕様に合わせる)

Start/Stopクリック

######## Start iperf (start_clk) ########
def start_clk(frm, arg):
    global log, fh
    proto = frm.sel_proto.get()	# 0:UDP 1:TCP
    # Log
    if Doing[arg] == 0:
        log = logging.getLogger(log_list[arg])
        log.setLevel(logging.DEBUG)
        fmt = '{:_%Y%m%d-%H%M%S}.csv'.format(datetime.now())
        log_file = log_list[arg] + '_' + proto_list[proto].lower() + fmt
        fh = logging.FileHandler(log_file)
        log.addHandler(fh)
        if debug:
            if arg == 0:	# Client
                raw_input = 'Input > ' + proto_list[proto]\
                    + ' ip: ' + frm.entryIP.get()\
                    + ' port:' + frm.entryPort.get()\
                    + ' bw: ' + frm.entryBandwidth.get()\
                    + ' len: ' + frm.entryLength.get()\
                    + ' time: ' + frm.entrySecond.get()
            else:		# Server
                raw_input = 'Input > ' + proto_list[proto]\
                    + ' port:' + frm.entryPort.get()
            #
            log.debug(raw_input)
        #
    #
    try:
        port = int(frm.entryPort.get())
        if port < 1 or port > 65535:
            port = default_port
        #
    except:
        port = default_port
    if arg == 0:	# Client
        ip = frm.entryIP.get()
        if is_valid_ip(ip) == False:
            ip = default_ip	
        bw = frm.entryBandwidth.get()
        if is_valid_bandwidth(bw) == False:
            bw = ''		# Not specified
        try:
            len = int(frm.entryLength.get())
        except:
            len = 0		# Not specified
        try:
            sec = int(frm.entrySecond.get())
        except:
            sec = default_time
    else:		# Server
        ip = ''			# dummy
        bw = ''			# dummy
        len = 0			# dummy
        sec = 0			# dummy
    #
    # ExecIperf
    ExecIperf(frm, arg, ip, proto, port, bw, len, sec)
    return

Start/Stopボタンクリック時の動作

  • プロトコル種別取得(UDP or TCP)
  • ログファイル設定
  • Debug ON時、入力値をログ化
  • Client・Serverに合わせて、IPアドレスやポートなどの入力値取得
  • 入力値チェック
  • エラー時や未入力時にデフォルト値セット
  • スレッドCall関数(ExecIperf())実行

スレッド呼び出し

######## Start Thread (ExecIperf) ########
def ExecIperf(frm, arg, ip, proto, port, bw, len, sec):
    global log, fh
    if Doing[arg] == 0:
        if debug:
            log.debug(tab_list[arg] + ' Start Clicked')
        #
        Doing[arg] = 1
        frm.Btn2.config(state='disabled')
        th = threading.Thread(target=IperfThread,\
                              args=(frm, arg, ip, proto, port, bw, len, sec),\
                              daemon=True)
        th.start()
    else:
        if 'proc' in globals():
            proc.terminate()
        #
        out = tab_list[arg] + 'Stopped\n'
        log.debug(out)
        frm.text.insert(END, out)
        frm.text.see('end')
        frm.Btn2.config(state='normal')
        fh.close()
        log.removeHandler(fh)
        Doing[arg] = 0
    frm.Btn['text'] = btn_list[Doing[arg]]
    return

スレッドCallまたは終了

  • 実行フラグONまたはOFF
  • Startクリック
    • スレッドCall(IperfThread())
  • Stopクリック
    • スレッド実行中であれば、スレッド停止
    • ログクローズ
  • ボタン無効化・有効化

iperf実行、結果表示、グラフ化、CSV化

######## Main Thread (IperfThread) ########
def IperfThread(frm, arg, ip, proto, port, bw, len, sec):
    global proc
    if arg == 0:	# Client
        cmd = 'iperf2.exe -c ' + ip
        if proto == proto_list.index('UDP'):
            cmd += ' -u'
        cmd += ' -p ' + str(port)
        if bw != '':
            cmd += ' -b ' + bw
        if len != 0:
            cmd += ' -l ' + str(len)
        cmd += ' -t ' + str(sec) + ' -i 1 -y C'
    else:		# Server
        cmd = 'iperf2.exe -s'
        if proto == proto_list.index('UDP'):
            cmd += ' -u'
        cmd += ' -p ' + str(port) + ' -i 1 -y C'
    #
    frm.cmd['text'] = '\tExecution: ' + cmd
    if debug:
        out = 'Execution: ' + cmd
        log.debug(out)
    #

実行前準備

  • 入力値からオプションセット
  • ”-y C”:出力をCSVフォーマット化
    frm.text.config(state='normal')
    # Header
    if proto == proto_list.index('UDP'):
        csv_header = ",".join(header)
        disp_header = '\t' + 'Interval\t\t' + 'Bandwidth\t\t'\
            + 'Jitter\t\t' + 'Loss Rate'
    else:			# TCP
        csv_header = ",".join(header[:9])    
        disp_header = '\t' + 'Interval\t\t' + 'Bandwidth\t\t'
    #
    log.debug(csv_header)
    frm.text.insert(END, disp_header + '\n')
    frm.text.see('end')

    # Clear Graph
    xpos.clear()
    ypos.clear()
    frm.ax.cla()
    frm.ax.grid() 
    xpos2.clear()
    ypos2.clear()
    frm.ax2.cla()
    frm.ax2.grid() 
    xpos3.clear()
    ypos3.clear()
    frm.ax3.cla()
    frm.ax3.grid() 

出力ヘッダおよびグラフ作成準備

  • 表示ヘッダ、CSVヘッダセット
  • グラフ初期化
    # Execute iperf2.exe
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    if debug:
        log.debug('Subprocess Executed')
    # 

iperf実行

  • subprocessにより”iperf2.exe”実行
    • 本コードと同一フォルダに”iperf2.exe”が存在する前提
    idx = 0
    lastX = 0
    while True:
        output = proc.stdout.readline()
        output = output.decode('utf-8')
        if output != '':   
            log.debug(output.rstrip('\r\n'))
            out_list = output.split(',')
            # try Start
            try:
                timeStr = out_list[header.index('Interval')]
                bwStr = out_list[header.index('Bandwidth(bps)')]
                bw = round(float(bwStr)/1000000, 2)
                X = float(timeStr.split('-')[0]) + 1.0
                Y = bw
                if proto == proto_list.index('UDP'):
                    jitterStr = out_list[header.index('Jitter(ms)')]
                    lossStr = out_list[header.index('Loss Rate(%)')]
                    out = '\t' + timeStr + '\t\t' + str(bw) + 'Mbps\t\t'\
                        + jitterStr + 'ms\t\t' + lossStr + '%'
                    Y2 = float(jitterStr)
                    Y3 = float(lossStr)
                else:		# TCP
                    out = '\t' + timeStr + '\t\t' + str(bw) + 'Mbps\t\t'
                #
                if X > lastX:
                    xpos.append(X+idx)
                    ypos.append(Y)
                    frm.ax.set_xlabel("Time (s)") 
                    frm.ax.set_ylabel("Bandwidth (Mbps)") 
                    frm.ax.plot(xpos, ypos, color='blue', linestyle='-')
                    if proto == proto_list.index('UDP'):
                        xpos2.append(X+idx)
                        ypos2.append(Y2)
                        xpos3.append(X+idx)
                        ypos3.append(Y3)
                        frm.ax2.set_xlabel("Time (s)") 
                        frm.ax2.set_ylabel("Jitter (ms)") 
                        frm.ax2.plot(xpos2, ypos2, color='green', linestyle='-')
                        frm.ax3.set_xlabel("Time (s)") 
                        frm.ax3.set_ylabel("Loss (%)") 
                        frm.ax3.plot(xpos3, ypos3, color='red', linestyle='-')
                    #
                    frm.graph.draw()
                    frm.graph2.draw()	# UDPの次にTCP実行時にJitterおよびLossのグラフを削除
                    frm.graph3.draw()	#
                    lastX = X
                else:	# Client Sending End
                    idx += lastX
                    lastX = 0
            # try Eend & except Start
            except:
                out = output
            # except End
            frm.text.insert(END, out + '\n')
            frm.text.see('end')
        else:
            if (proc.poll() is not None) or (Doing[arg] == 0):
                break
    #

結果(出力)処理

  • インデックス初期化
  • 出力がある間、1行ずつ処理(readline())
    • 出力(CSV)をそのままログ(ファイル)化
    • CSVから表示項目を抜き出し(共通:時間、帯域、UDPの場合:JitterおよびLoss)
    • グラフ化用位置変数に、時間・帯域・Jitter・Lossデータを格納
    • グラフ化(draw())
    • 表示項目をテキストボックスに表示
    frm.text.config(state='disabled')
    Doing[arg] = 0
    frm.Btn['text'] = btn_list[Doing[arg]]
    frm.Btn2.config(state='normal')
    if debug:
        log.debug('Thread End')
    fh.close()
    log.removeHandler(fh)
    return

終了処理

  • 実行フラグOFF
  • ボタン無効化
  • ログクローズ

グラフ保存クリック

######## Start iperf (save_clk) ########
def save_clk(frm, arg):
    prefix = log_list[arg]
    proto = frm.sel_proto.get()	# 0:UDP 1:TCP
    datetimestr = '{:%Y%m%d_%H%M%S}'.format(datetime.now())
    bwstr = '_' + proto_list[proto].lower() + '_bandwidth_'
    frm.fig.savefig(prefix + bwstr + datetimestr + '.png')
    if proto == proto_list.index('UDP'):
        frm.fig2.savefig(prefix + '_udp_jitter_' + datetimestr + '.png')
        frm.fig3.savefig(prefix + '_udp_loss_' + datetimestr + '.png')
    #
    messagebox.showinfo('Message', 'Save Complete') 
    return
  • 日時取得
  • 日時をファイル名に含めて結果グラフを保存
    • JitterおよびLossはUDP時のみ
  • 保存完了メッセージボックス

全体

iperfGraphBeta.py
# -*- coding: utf-8 -*-

## Version
version = 'Beta'

## Import
import sys
import threading
import subprocess
import logging
import logging.handlers
import time
from datetime import datetime
from socket import inet_aton
import re
from tkinter import *
import tkinter.ttk as ttk
import tkinter.messagebox as messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 
from matplotlib.figure import Figure 

## List
tab_list = [' Client ', ' Server ']
btn_list = [' Start ', ' Stop ']
log_list = ['iperf-client', 'iperf-server']
proto_list = ['UDP', 'TCP']

## Flag
Doing = [0, 0] 			# Client, Server

## Default Parameters
default_ip = '127.0.0.1'	# Loopback
default_port = 5001		# iperf2 default port
default_time = 10		# 10 sec

## CSV Header (UDP: All,  TCP: until by Bandwidth)
header = ['Date & Time', 'Src IP', 'Src Port', 'Dst IP', 'Dst Port',\
          'ID', 'Interval', 'Transfer(byte)', 'Bandwidth(bps)',\
          'Jitter(ms)', 'Lost', 'Total', 'Loss Rate(%)']

## For plotting
xpos = []
xpos2 = []
xpos3 = []
ypos = []
ypos2 = []
ypos3 = []

######## Main Thread (IperfThread) ########
def IperfThread(frm, arg, ip, proto, port, bw, len, sec):
    global proc
    if arg == 0:	# Client
        cmd = 'iperf2.exe -c ' + ip
        if proto == proto_list.index('UDP'):
            cmd += ' -u'
        cmd += ' -p ' + str(port)
        if bw != '':
            cmd += ' -b ' + bw
        if len != 0:
            cmd += ' -l ' + str(len)
        cmd += ' -t ' + str(sec) + ' -i 1 -y C'
    else:		# Server
        cmd = 'iperf2.exe -s'
        if proto == proto_list.index('UDP'):
            cmd += ' -u'
        cmd += ' -p ' + str(port) + ' -i 1 -y C'
    #
    frm.cmd['text'] = '\tExecution: ' + cmd
    if debug:
        out = 'Execution: ' + cmd
        log.debug(out)
    #
    frm.text.config(state='normal')
    # Header
    if proto == proto_list.index('UDP'):
        csv_header = ",".join(header)
        disp_header = '\t' + 'Interval\t\t' + 'Bandwidth\t\t'\
            + 'Jitter\t\t' + 'Loss Rate'
    else:			# TCP
        csv_header = ",".join(header[:9])    
        disp_header = '\t' + 'Interval\t\t' + 'Bandwidth\t\t'
    #
    log.debug(csv_header)
    frm.text.insert(END, disp_header + '\n')
    frm.text.see('end')

    # Clear Graph
    xpos.clear()
    ypos.clear()
    frm.ax.cla()
    frm.ax.grid() 
    xpos2.clear()
    ypos2.clear()
    frm.ax2.cla()
    frm.ax2.grid() 
    xpos3.clear()
    ypos3.clear()
    frm.ax3.cla()
    frm.ax3.grid() 

    # Execute iperf2.exe
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    if debug:
        log.debug('Subprocess Executed')
    # 
    idx = 0
    lastX = 0
    while True:
        output = proc.stdout.readline()
        output = output.decode('utf-8')
        if output != '':   
            log.debug(output.rstrip('\r\n'))
            out_list = output.split(',')
            # try Start
            try:
                timeStr = out_list[header.index('Interval')]
                bwStr = out_list[header.index('Bandwidth(bps)')]
                bw = round(float(bwStr)/1000000, 2)
                X = float(timeStr.split('-')[0]) + 1.0
                Y = bw
                if proto == proto_list.index('UDP'):
                    jitterStr = out_list[header.index('Jitter(ms)')]
                    lossStr = out_list[header.index('Loss Rate(%)')]
                    out = '\t' + timeStr + '\t\t' + str(bw) + 'Mbps\t\t'\
                        + jitterStr + 'ms\t\t' + lossStr + '%'
                    Y2 = float(jitterStr)
                    Y3 = float(lossStr)
                else:		# TCP
                    out = '\t' + timeStr + '\t\t' + str(bw) + 'Mbps\t\t'
                #
                if X > lastX:
                    xpos.append(X+idx)
                    ypos.append(Y)
                    frm.ax.set_xlabel("Time (s)") 
                    frm.ax.set_ylabel("Bandwidth (Mbps)") 
                    frm.ax.plot(xpos, ypos, color='blue', linestyle='-')
                    if proto == proto_list.index('UDP'):
                        xpos2.append(X+idx)
                        ypos2.append(Y2)
                        xpos3.append(X+idx)
                        ypos3.append(Y3)
                        frm.ax2.set_xlabel("Time (s)") 
                        frm.ax2.set_ylabel("Jitter (ms)") 
                        frm.ax2.plot(xpos2, ypos2, color='green', linestyle='-')
                        frm.ax3.set_xlabel("Time (s)") 
                        frm.ax3.set_ylabel("Loss (%)") 
                        frm.ax3.plot(xpos3, ypos3, color='red', linestyle='-')
                    #
                    frm.graph.draw()
                    frm.graph2.draw()	# UDPの次にTCP実行時にJitterおよびLossのグラフを削除
                    frm.graph3.draw()	#
                    lastX = X
                else:	# Client Sending End
                    idx += lastX
                    lastX = 0
            # try Eend & except Start
            except:
                out = output
            # except End
            frm.text.insert(END, out + '\n')
            frm.text.see('end')
        else:
            if (proc.poll() is not None) or (Doing[arg] == 0):
                break
    #
    frm.text.config(state='disabled')
    Doing[arg] = 0
    frm.Btn['text'] = btn_list[Doing[arg]]
    frm.Btn2.config(state='normal')
    if debug:
        log.debug('Thread End')
    fh.close()
    log.removeHandler(fh)
    return

######## Start Thread (ExecIperf) ########
def ExecIperf(frm, arg, ip, proto, port, bw, len, sec):
    global log, fh
    if Doing[arg] == 0:
        if debug:
            log.debug(tab_list[arg] + ' Start Clicked')
        #
        Doing[arg] = 1
        frm.Btn2.config(state='disabled')
        th = threading.Thread(target=IperfThread,\
                              args=(frm, arg, ip, proto, port, bw, len, sec),\
                              daemon=True)
        th.start()
    else:
        if 'proc' in globals():
            proc.terminate()
        #
        out = tab_list[arg] + 'Stopped\n'
        log.debug(out)
        frm.text.insert(END, out)
        frm.text.see('end')
        frm.Btn2.config(state='normal')
        fh.close()
        log.removeHandler(fh)
        Doing[arg] = 0
    frm.Btn['text'] = btn_list[Doing[arg]]
    return

######## Start iperf (save_clk) ########
def save_clk(frm, arg):
    prefix = log_list[arg]
    proto = frm.sel_proto.get()	# 0:UDP 1:TCP
    datetimestr = '{:%Y%m%d_%H%M%S}'.format(datetime.now())
    bwstr = '_' + proto_list[proto].lower() + '_bandwidth_'
    frm.fig.savefig(prefix + bwstr + datetimestr + '.png')
    if proto == proto_list.index('UDP'):
        frm.fig2.savefig(prefix + '_udp_jitter_' + datetimestr + '.png')
        frm.fig3.savefig(prefix + '_udp_loss_' + datetimestr + '.png')
    #
    messagebox.showinfo('Message', 'Save Complete') 
    return

######## Check IP Address Format (is_valid_ip) ########
def is_valid_ip(addr):
    try:
        inet_aton(addr)
        return True
    except:
        return False

######## Bandwidth Parameter Check (is_valid_bandwidth) ########
def is_valid_bandwidth(arg):
    if arg.count('.') <= 1:	# Only 1 decimal point
        m = re.match(r'[0-9][0-9.]*[kKmMgG]', arg)
        if m == None:
            return False
        else:
            return True
    else:
        return False
    
######## Start iperf (start_clk) ########
def start_clk(frm, arg):
    global log, fh
    proto = frm.sel_proto.get()	# 0:UDP 1:TCP
    # Log
    if Doing[arg] == 0:
        log = logging.getLogger(log_list[arg])
        log.setLevel(logging.DEBUG)
        fmt = '{:_%Y%m%d-%H%M%S}.csv'.format(datetime.now())
        log_file = log_list[arg] + '_' + proto_list[proto].lower() + fmt
        fh = logging.FileHandler(log_file)
        log.addHandler(fh)
        if debug:
            if arg == 0:	# Client
                raw_input = 'Input > ' + proto_list[proto]\
                    + ' ip: ' + frm.entryIP.get()\
                    + ' port:' + frm.entryPort.get()\
                    + ' bw: ' + frm.entryBandwidth.get()\
                    + ' len: ' + frm.entryLength.get()\
                    + ' time: ' + frm.entrySecond.get()
            else:		# Server
                raw_input = 'Input > ' + proto_list[proto]\
                    + ' port:' + frm.entryPort.get()
            #
            log.debug(raw_input)
        #
    #
    try:
        port = int(frm.entryPort.get())
        if port < 1 or port > 65535:
            port = default_port
        #
    except:
        port = default_port
    if arg == 0:	# Client
        ip = frm.entryIP.get()
        if is_valid_ip(ip) == False:
            ip = default_ip	
        bw = frm.entryBandwidth.get()
        if is_valid_bandwidth(bw) == False:
            bw = ''		# Not specified
        try:
            len = int(frm.entryLength.get())
        except:
            len = 0		# Not specified
        try:
            sec = int(frm.entrySecond.get())
        except:
            sec = default_time
    else:		# Server
        ip = ''			# dummy
        bw = ''			# dummy
        len = 0			# dummy
        sec = 0			# dummy
    #
    # ExecIperf
    ExecIperf(frm, arg, ip, proto, port, bw, len, sec)
    return

######## For Debug Log ########
def log_tgl():
    global debug
    tgl.set(not tgl.get())
    if tgl.get():
        debug = True
    else:
        debug = False
    return

######## Client&Server Tab (create_content) ########
def create_content(frm, arg):
    frm1 = Frame(frm)
    frm2 = Frame(frm)
    frm3 = Frame(frm)
    frm4 = Frame(frm)
    frm5 = Frame(frm)

    # Frame 1
    if arg == 0:	# Client
       label1 = Label(frm1, text=' IP address ')
       label1.grid(row=0, column=0)
       frm.entryIP = Entry(frm1, width=16)
       frm.entryIP.grid(row=0, column=1)
       frm.sel_proto = IntVar(value=0)
       rb_udp = Radiobutton(frm1, text=" UDP ", variable=frm.sel_proto, value=0)
       rb_udp.grid(row=0, column=2)
       rb_tcp = Radiobutton(frm1, text=" TCP ", variable=frm.sel_proto, value=1)
       rb_tcp.grid(row=0, column=3)
       label2 = Label(frm1, text=' Port ')
       label2.grid(row=0, column=4)
       frm.entryPort = Entry(frm1, width=6)
       frm.entryPort.grid(row=0, column=5)
       label3 = Label(frm1, text=' Bandwith ')
       label3.grid(row=0, column=6)
       frm.entryBandwidth = Entry(frm1, width=6)
       frm.entryBandwidth.grid(row=0, column=7)
       label4 = Label(frm1, text=' Length ')
       label4.grid(row=0, column=8)
       frm.entryLength = Entry(frm1, width=6)
       frm.entryLength.grid(row=0, column=9)
       label5 = Label(frm1, text=' Time(sec) ')
       label5.grid(row=0, column=10)
       frm.entrySecond = Entry(frm1, width=6)
       frm.entrySecond.grid(row=0, column=11)
       label6 = Label(frm1, text=' ')
       label6.grid(row=0, column=12)
       frm.Btn = Button(frm1, text=btn_list[0], command=lambda:start_clk(frm, arg))
       frm.Btn.grid(row=0, column=13)
       label7 = Label(frm1, text=' ')
       label7.grid(row=0, column=14)
       frm.Btn2 = Button(frm1, text=' Graph Save ', command=lambda:save_clk(frm, arg))
       frm.Btn2.grid(row=0, column=15)
       frm.cmd = Label(frm1, text='\t\t\t\t\t\t\t')
       frm.cmd.grid(row=0, column=16)
    else:		# Server
       frm.sel_proto = IntVar(value=0)
       rb_udp = Radiobutton(frm1, text=" UDP ", variable=frm.sel_proto, value=0)
       rb_udp.grid(row=0, column=0)
       rb_tcp = Radiobutton(frm1, text=" TCP ", variable=frm.sel_proto, value=1)
       rb_tcp.grid(row=0, column=1)
       label1 = Label(frm1, text=' Port ')
       label1.grid(row=0, column=2)
       frm.entryPort = Entry(frm1, width=6)
       frm.entryPort.grid(row=0, column=3)
       label2 = Label(frm1, text=' ')
       label2.grid(row=0, column=4)
       frm.Btn = Button(frm1, text=btn_list[0], command=lambda:start_clk(frm, arg))
       frm.Btn.grid(row=0, column=5)
       label3 = Label(frm1, text=' ')
       label3.grid(row=0, column=6)
       frm.Btn2 = Button(frm1, text=' Graph Save ', command=lambda:save_clk(frm, arg))
       frm.Btn2.grid(row=0, column=7)
       frm.cmd = Label(frm1, text='\t\t\t\t\t\t')
       frm.cmd.grid(row=0, column=8)
    frm1.grid(row=0, column=0, columnspan=2, sticky="n")
    
    # Frame 2
    frm.text = Text(frm2, width=70, height=13, font=("",12,'bold'), bg="#ffe", bd=2)
    frm2.ysc = Scrollbar(frm2, orient=VERTICAL, command=frm.text.yview)
    frm.text["yscrollcommand"] = frm2.ysc.set
    frm2.ysc.grid(row=0, column=1, sticky="ns")
    frm.text.config(state='disabled')
    frm.text.grid(row=0, column=0, sticky="nsew")
    frm2.grid(row=1, column=0, sticky="nsew")

    # Frame 3
    frm.fig = Figure() 
    frm.ax = frm.fig.add_subplot(111) 
    frm.ax.set_xlabel("Time (s)") 
    frm.ax.set_ylabel("Bandwidth (Mbps)")
    frm.fig.tight_layout()  		# レイアウト調整
    frm.fig.subplots_adjust(bottom=0.2)	# 下側に余白を確保
    frm.fig.subplots_adjust(left=0.15)
    frm.graph = FigureCanvasTkAgg(frm.fig, master=frm3)
    frm.graph.get_tk_widget().grid(row=0, column=0, sticky="nsew")
    frm3.grid(row=1, column=1, sticky="nsew")

    # Frame 4
    frm.fig2 = Figure() 
    frm.ax2 = frm.fig2.add_subplot(111) 
    frm.ax2.set_xlabel("Time (s)") 
    frm.ax2.set_ylabel("Jitter (ms)") 
    frm.fig2.tight_layout()
    frm.fig2.subplots_adjust(bottom=0.4)
    frm.fig2.subplots_adjust(left=0.15)
    frm.graph2 = FigureCanvasTkAgg(frm.fig2, master=frm4)
    frm.graph2.get_tk_widget().grid(row=0, column=0, sticky="nsew")
    frm4.grid(row=2, column=0, sticky="nsew")

    # Frame 5
    frm.fig3 = Figure() 
    frm.ax3 = frm.fig3.add_subplot(111) 
    frm.ax3.set_xlabel("Time (s)") 
    frm.ax3.set_ylabel("Loss (%)") 
    frm.fig3.tight_layout()
    frm.fig3.subplots_adjust(bottom=0.4)
    frm.fig3.subplots_adjust(left=0.15)
    frm.graph3 = FigureCanvasTkAgg(frm.fig3, master=frm5)
    frm.graph3.get_tk_widget().grid(row=0, column=0, sticky="nsew")
    frm5.grid(row=2, column=1, sticky="nsew")

    # グリッドの調整(リサイズ対応)
    frm.grid_columnconfigure(0, weight=1)
    frm.grid_columnconfigure(1, weight=1)
    frm.grid_rowconfigure(1, weight=1)
    frm.grid_rowconfigure(2, weight=1)

    frm3.grid_rowconfigure(0, weight=1)
    frm4.grid_rowconfigure(0, weight=1)
    frm5.grid_rowconfigure(0, weight=1)

    return

############### Start of main program ###############
# root main window
root = Tk()
ver_str = 'iperfGraph ' + version
root.title(ver_str)
root.geometry("1300x600")

# Create an instance of ttk style
fgcolor = "lightskyblue2"
bgcolor = "gray80"
style = ttk.Style()
style.theme_create("style1", parent="alt", settings={
    "TNotebook.Tab": {
        "configure": {"background": bgcolor },
        "map":       {"background": [("selected", fgcolor)],
                      } },
    "TMenu": {
        "configure": {"background": "red" },
    } } )
style.theme_use("style1")

# Create Notebook Widget
note = ttk.Notebook(root)

# Create tab
tab0 = Frame(note)	# Client
tab1 = Frame(note)	# Server

# Add tab
note.add(tab0, text=tab_list[0])
note.add(tab1, text=tab_list[1])

# Create tab content
create_content(tab0, 0)	# Client
create_content(tab1, 1)	# Server

# Locate tab
note.pack(expand=True, fill='both')

# Menu
debug = False
tgl = BooleanVar(value = debug)
menubar = Menu()
filemenu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=filemenu)
filemenu.add_command(label="Exit", command=sys.exit)
filemenu.add_checkbutton(label="Debug", command=log_tgl)
root.config(menu=menubar)

# main loop
root.mainloop()

######## End of loop ########

EOF

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?