iperfの結果をリアルタイム表示
Pythonにて、iperfの結果をリアルタイム表示に表示する。tkinter、subprocess、matplotlibを利用。それぞれ、小生の過去の記事をもとに作成した。
- ろうとるがPythonを扱う、、(その4:まとも版コマンドプロンプトもどき)
- ろうとるがPythonを扱う、、(その8:tkinter&Subprocessでの割り込み)
- ろうとるがPythonを扱う、、(その27:tkinter+matplotlibでRealtimeグラフ表示(その2))
レイアウト(フレーム配置)については、適宜、ChatGPTに相談。なお、Windowsのiperf Version2を利用。Version3を使わなかった背景は下記参照。
なお、本コード(またはEXE)が存在するフォルダーに実体である”iperf2.exe”が存在することが前提である。
結果
まずは結果から記載。
Client
Server
Bandwidthはすべてに共通であるが、JitterおよびLossはUDP Serverのみに適用される。
ファイル
”Graph Save”をクリックするとグラフが保存される。また、iperfの結果をCSVファイルに保存する。下記は作成されたファイル。
CSVファイルサンプルは下記である。
グラフファイルサンプルは下記。
ソースコード
いつものように、ポイントを記載。
ライブラリ
## 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つのフレーム構成。
- 入力やボタン
- iperfの実行結果表示ボックス
- Bandwidthグラフ
- Jitterグラフ
- 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時のみ
- 保存完了メッセージボックス
全体
# -*- 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




