import socket
import threading
import tkinter as tk
from tkinter import filedialog
import logging
import time

logging.basicConfig(filename='TCPClient.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')

packet_file_path = ''

# 读取文件中的16进制报文
def read_hex_packets(file_path):
packets = []
try:
with open(file_path, "r") as f:
lines = f.readlines()
for line in lines:
# 缩进块要注意缩进的空格数,通常为四个空格
line = line.strip()
if not line:
continue
if len(line) % 2 == 1:
line = "0" + line
try:
packet = bytes.fromhex(line)
except ValueError:
logging.error("无效的报文格式: %s", line)
# continue
packets.append(packet)
except IOError as e:
logging.error("读取文件 %s 失败: %s", file_path, str(e))
return packets

# 发送报文并打印发送和接收的报文
def send_packets():
# 获取输入的服务器ip和端口号
server_ip = server_ip_entry.get()
server_port = int(server_port_entry.get())

# 获取是否开启"发送每一帧报文之后都等待服务器回传数据"功能的勾选状态
wait_for_response = wait_for_response_var.get()

# 获取报文发送间隔
send_interval = int(send_interval_entry.get())

# 读取16进制报文
packets = read_hex_packets(packet_file_path)

# 创建TCP客户端socket
try:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error as e:
logging.error("创建socket失败: %s", str(e))
return

try:
# 连接服务器
client_socket.connect((server_ip, server_port))

# 清空发送和接收框
text.delete(1.0,tk.END)
response_text.delete(1.0,tk.END)

for i, packet in enumerate(packets):
# 发送报文
try:
client_socket.sendall(packet)
except socket.error as e:
logging.error("发送第%d帧报文失败: %s", i+1, str(e))
continue
logging.info("发送第%d帧报文: %s", i+1, packet.hex())

# 等待服务器回传
if wait_for_response:
try:
response_packet = client_socket.recv(1024)
logging.info("收到第%d帧回传报文: %s", i+1, response_packet.hex())
response_text.insert(tk.END, response_packet.hex() + "\n")
except socket.error as e:
logging.error("接收第%d帧回传报文失败: %s", i+1, str(e))

# 等待一定时间再发送下一帧
if i != len(packets)-1:
time.sleep(send_interval / 1000)

except socket.error as e:
logging.error(str(e))

finally:
# 关闭socket连接
client_socket.close()

# 打开文件对话框
def open_file_dialog():
global packet_file_path
packet_file_path = filedialog.askopenfilename(filetypes=(("Text files", "*.txt"),))
file_path_label.config(text=packet_file_path)

# 创建GUI界面
root = tk.Tk()
root.title("TCP客户端")

# 服务器ip和端口号输入框
server_frame = tk.Frame()
server_frame.pack(side=tk.TOP, padx=10, pady=10)
server_ip_label = tk.Label(server_frame, text="服务器IP: ")
server_ip_label.pack(side=tk.LEFT)
server_ip_entry = tk.Entry(server_frame, width=15)
server_ip_entry.pack(side=tk.LEFT)
server_port_label = tk.Label(server_frame, text="服务器端口号: ")
server_port_label.pack(side=tk.LEFT)
server_port_entry = tk.Entry(server_frame, width=5)
server_port_entry.pack(side=tk.LEFT)

# 选择报文文件和发送间隔输入框
file_frame = tk.Frame()
file_frame.pack(side=tk.TOP, padx=10, pady=10)
open_file_button = tk.Button(file_frame, text="选择报文文件", command=open_file_dialog)
open_file_button.pack(side=tk.LEFT)
file_path_label = tk.Label(file_frame, text="尚未选择文件")
file_path_label.pack(side=tk.LEFT, padx=10)
send_interval_label = tk.Label(file_frame, text="发送间隔(ms): ")
send_interval_label.pack(side=tk.LEFT)
send_interval_entry = tk.Entry(file_frame, width=5)
send_interval_entry.pack(side=tk.LEFT)

# "发送每一帧报文之后都等待服务器回传数据"勾选框
wait_for_response_var = tk.BooleanVar()
wait_for_response_checkbox = tk.Checkbutton(root, text="发送每一帧报文之后都等待服务器回传数据", variable=wait_for_response_var)
wait_for_response_checkbox.pack(side=tk.TOP)

# 发送和接收框
text_frame = tk.Frame()
text_frame.pack(side=tk.TOP, padx=10, pady=10)
text = tk.Text(text_frame, height=10, width=50)
text.pack(side=tk.LEFT)
response_text = tk.Text(text_frame, height=10, width=50)
response_text.pack(side=tk.RIGHT)

# 发送按钮
send_button = tk.Button(root, text="发送", command=send_packets)
send_button.pack(side=tk.BOTTOM, pady=10)

root.mainloop()