import tkinter as tk
import ttkbootstrap as tb
import threading
import socket
import subprocess
import time
from ttkbootstrap.constants import *
from PIL import Image, ImageTk, ImageDraw
from tkinter import scrolledtext
from datetime import datetime
# 数据接收器类
class MLUDataReceiver:
def __init__(self, callback):
self.callback = callback
self.devices = {
"设备1": {
"ip": "192.168.100.50",
"status": "离线",
"temperature": 0,
"power": 0,
"memory": 0,
"bandwidth": 0,
"mlu_avg_usage": 0,
"cpu_avg_usage": 0
}
}
self.current_device = "设备1"
self.running = True
self.start_udp_receive()
def parse_info(self, info_str):
info = {}
sections = info_str.split("---------------END---------------")[0].split("\n\n")
for section in sections:
lines = [line.strip() for line in section.splitlines() if line.strip()]
if not lines:
continue
key = lines[0].split(":")[0].strip()
if key == "温度信息":
info.update(self.parse_temperature(lines[1:]))
elif key == "功率信息":
info.update(self.parse_power(lines[1:]))
elif key == "内存信息":
info.update(self.parse_memory(lines))
elif key == "带宽信息":
info.update(self.parse_bandwidth(lines[1:]))
elif key == "MLU信息":
# 解析MLU核心利用率
mlu_info = self.parse_mlu_info(lines)
info.update(mlu_info)
elif key == "CPU信息":
# 解析CPU核心利用率
cpu_info = self.parse_cpu_info(lines)
info.update(cpu_info)
return info
def parse_temperature(self, lines):
temp = 0
for line in lines:
if "C" in line:
try:
temp = max(temp, float(line.split(":")[1].strip().replace(" C", "")))
except (IndexError, ValueError):
continue
return {"temperature": temp}
def parse_power(self, lines):
power = 0
for line in lines:
if "W" in line:
try:
power = float(line.split(":")[1].strip().replace(" W", ""))
except (IndexError, ValueError):
continue
return {"power": power}
def parse_memory(self, lines):
total = 0
used = 0
physical_memory_section = False
for line in lines:
if "Physical Memory Usage" in line:
physical_memory_section = True
elif physical_memory_section:
if "Total" in line:
total = self.parse_memory_value(line)
elif "Used" in line:
used = self.parse_memory_value(line)
return {"memory": round(used / total * 100, 1) if total != 0 else 0}
def parse_memory_value(self, line):
try:
value = line.split(":")[1].strip()
num = float(value.split()[0])
unit = value.split()[1]
if unit == "MiB":
return num * 1024 * 1024
return num
except (IndexError, ValueError):
return 0
def parse_bandwidth(self, lines):
bandwidth = 0
for line in lines:
if "GB/s" in line:
try:
bandwidth = float(line.split(":")[1].strip().replace(" GB/s", ""))
except (IndexError, ValueError):
continue
return {"bandwidth": bandwidth}
def parse_mlu_info(self, lines):
"""解析MLU信息,包括平均利用率和各核心利用率"""
mlu_avg_usage = 0
mlu_cores = [0.0] * 4 # 初始化4个核心的利用率
for line in lines:
if "MLU Average" in line:
try:
parts = line.split(':')
if len(parts) > 1:
mlu_avg_usage = float(parts[1].strip().replace("%", ""))
except (IndexError, ValueError):
continue
# 解析各核心利用率
for i in range(4):
if f"MLU {i}" in line:
try:
parts = line.split(':')
if len(parts) > 1:
mlu_cores[i] = float(parts[1].strip().replace("%", ""))
except (IndexError, ValueError):
continue
return {
"mlu_avg_usage": mlu_avg_usage,
"mlu_cores": mlu_cores
}
def parse_cpu_info(self, lines):
"""解析CPU信息,包括平均利用率和各核心利用率"""
cpu_avg_usage = 0
cpu_cores = [0.0] * 4 # 初始化4个核心的利用率
for line in lines:
if "Device CPU Chip" in line:
try:
parts = line.split(':')
if len(parts) > 1:
cpu_avg_usage = float(parts[1].strip().replace("%", ""))
except (IndexError, ValueError):
continue
# 解析各核心利用率
for i in range(4):
if f"Device CPU Core {i}" in line:
try:
parts = line.split(':')
if len(parts) > 1:
cpu_cores[i] = float(parts[1].strip().replace("%", ""))
except (IndexError, ValueError):
continue
return {
"cpu_avg_usage": cpu_avg_usage,
"cpu_cores": cpu_cores
}
def start_udp_receive(self):
def read_config():
config = {}
try:
with open('config.txt', 'r') as config_file:
for line in config_file:
if '=' in line:
key, value = line.strip().split('=', 1)
config[key] = value
print("Read config success.")
except FileNotFoundError:
print("Unable to open config file!")
return None
return config
config = read_config()
if config is None:
return
server_ip = config.get('SERVER_IP')
server_port = int(config.get('SERVER_PORT'))
client_ip = config.get('CLIENT_IP')
client_port = int(config.get('CLIENT_PORT'))
print("ServerIP: {}".format(server_ip))
print("ServerPort: {}".format(server_port))
print("ClientIP: {}".format(client_ip))
print("ClientPort: {}".format(client_port))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("Set socket ok.")
try:
sock.bind((client_ip, client_port))
print("Bind success.")
except OSError:
print("Bind error!")
return
def receive_data():
print("Start receive data.")
try:
while self.running:
data, addr = sock.recvfrom(4096)
try:
info_str = data.decode('utf-8')
device = self.devices[self.current_device]
# ping检测逻辑
ip = device["ip"]
try:
result = subprocess.run(['ping', '-c', '1', '-W', '1', ip],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode == 0:
device["status"] = "在线"
else:
device["status"] = "离线"
except Exception as e:
print(f"Ping 检测出错: {e}")
device["status"] = "离线"
# 解析数据并更新设备信息
info = self.parse_info(info_str)
device.update(info)
# 通过回调传递数据
self.callback({
'cpu_temp': device['temperature'],
'power': device['power'],
'mem_usage': device['memory'],
'bandwidth': device['bandwidth'] * 8000, # 转换为Mbps
'mlu_usage': device['mlu_avg_usage'],
'mlu_cores': device.get('mlu_cores', [0, 0, 0, 0]),
'cpu_usage': device['cpu_avg_usage'],
'cpu_cores': device.get('cpu_cores', [0, 0, 0, 0]),
'status': device['status']
})
except UnicodeDecodeError:
print("解码数据时出错,请检查数据编码。")
except OSError:
print("Receive data error!")
except Exception as e:
print(f"Unexpected error: {e}")
finally:
sock.close()
receive_thread = threading.Thread(target=receive_data)
receive_thread.daemon = True
receive_thread.start()
def stop(self):
self.running = False
# 增强型圆形进度条类
class EnhancedCircularProgressBar:
def __init__(self, parent, size=200, thickness=20,
font_size=16, title="", unit="", max_value=100):
self.parent = parent
self.size = size
self.thickness = thickness
self.font_size = font_size
self.title = title
self.unit = unit
self.max_value = max_value
self.current_value = 0
self.target_value = 0
self.animation_speed = 1.0 # 动画速度因子
# 获取当前主题颜色
style = tb.Style()
self.text_color = style.colors.fg
self.bg_color = style.colors.bg
self.secondary_bg = style.colors.inputbg # 背景圆环使用输入框背景色
# 创建画布
self.canvas = tk.Canvas(
parent,
width=size,
height=size,
highlightthickness=0,
bd=0,
bg=self.bg_color # 使用主题背景色
)
# 计算圆心和半径
self.center_x = size / 2
self.center_y = size / 2
self.radius = (size - thickness) / 2 - 5
# 绘制背景圆环
self.draw_background()
# 绘制文本
self.text_id = self.canvas.create_text(
self.center_x,
self.center_y,
text="0%",
fill=self.text_color, # 使用主题前景色
font=("Arial", font_size, "bold")
)
# 绘制标题
self.title_id = self.canvas.create_text(
self.center_x,
self.center_y + 40,
text=title,
fill=self.text_color, # 使用主题前景色
font=("Arial", int(font_size*0.8))
)
# 初始绘制
self.set_value(0)
def draw_background(self):
"""绘制背景圆环"""
# 计算圆环的外接矩形坐标
x0 = self.center_x - self.radius
y0 = self.center_y - self.radius
x1 = self.center_x + self.radius
y1 = self.center_y + self.radius
# 绘制背景圆环
self.bg_arc = self.canvas.create_arc(
x0, y0, x1, y1,
start=0, # 起始角度
extent=360, # 360度完整圆环
width=self.thickness,
outline=self.secondary_bg, # 使用主题输入框背景色
style=tk.ARC
)
def interpolate_color(self, progress):
"""根据进度值插值计算颜色(绿-黄-橙-红过渡)"""
# 定义颜色过渡点
color_points = [
(0.00, (0, 180, 0)), # 深绿色
(0.25, (170, 255, 0)), # 黄绿色
(0.50, (255, 255, 0)), # 黄色
(0.75, (255, 170, 0)), # 橙色
(1.00, (255, 0, 0)) # 红色
]
# 找到当前进度所在的区间
for i in range(1, len(color_points)):
if progress <= color_points[i][0]:
# 计算区间内的比例
t = (progress - color_points[i-1][0]) / (color_points[i][0] - color_points[i-1][0])
# 线性插值计算RGB值(不使用numpy)
r1, g1, b1 = color_points[i-1][1]
r2, g2, b2 = color_points[i][1]
r = int(r1 + t * (r2 - r1))
g = int(g1 + t * (g2 - g1))
b = int(b1 + t * (b2 - b1))
return f"#{r:02x}{g:02x}{b:02x}"
# 默认返回红色
return "#ff0000"
def set_value(self, value, animate=True):
"""设置当前值并更新进度条"""
# 确保值在合理范围内
value = max(0, min(value, self.max_value))
if animate:
self.target_value = value
# 启动动画(如果尚未运行)
if abs(self.current_value - self.target_value) > 0.1:
self.animate_step()
else:
self.target_value = value
self.current_value = value
self.update_display()
def update_display(self):
"""更新进度条显示"""
# 计算进度比例
progress = self.current_value / self.max_value
# 计算角度(从-90度开始,即12点钟方向)
angle = min(progress * 360, 359.99)
# 清除旧的前景圆环
if hasattr(self, 'fg_arc'):
self.canvas.delete(self.fg_arc)
# 计算圆环的外接矩形坐标
x0 = self.center_x - self.radius
y0 = self.center_y - self.radius
x1 = self.center_x + self.radius
y1 = self.center_y + self.radius
# 获取插值颜色
color = self.interpolate_color(progress)
# 绘制前景圆环(进度指示)
self.fg_arc = self.canvas.create_arc(
x0, y0, x1, y1,
start=90, # 从12点钟方向开始
extent=angle, # 根据值计算角度
width=self.thickness,
outline=color,
style=tk.ARC
)
# 提升前景圆环的显示层级
self.canvas.tag_raise(self.fg_arc)
# 更新文本显示 - 修改带宽显示
if "带宽" in self.title:
# 对于带宽,显示实际值加单位
display_text = f"{self.current_value:.1f} {self.unit}"
elif self.unit == "%":
# 对于百分比,显示百分比值
display_text = f"{progress*100:.1f}%"
else:
# 其他情况显示数值加单位
display_text = f"{self.current_value:.1f} {self.unit}"
self.canvas.itemconfig(self.text_id, text=display_text)
def animate_step(self):
"""执行一步动画(不使用numpy)"""
if abs(self.current_value - self.target_value) < 0.1:
self.current_value = self.target_value
else:
# 使用缓动函数实现平滑动画
diff = self.target_value - self.current_value
self.current_value += diff * 0.2 * self.animation_speed
self.update_display()
# 如果未达到目标值,继续动画
if abs(self.current_value - self.target_value) > 0.1:
self.parent.after(16, self.animate_step) # 约60fps
# 详细窗口类
class DetailWindow:
def __init__(self, parent, title, data, core_data, core_type):
self.parent = parent
self.window = tb.Toplevel(parent)
self.window.title(title)
self.window.geometry("650x450")
self.window.resizable(True, True)
# 设置主题与主窗口一致
style = tb.Style()
current_theme = self.parent.style.theme_use()
style.theme_use(current_theme)
# 主框架
main_frame = tb.Frame(self.window, padding=10)
main_frame.pack(fill=tk.BOTH, expand=True)
# 标题
tb.Label(
main_frame,
text=title,
font=("Arial", 16, "bold"),
bootstyle=PRIMARY
).pack(pady=(0, 15))
# 平均利用率
avg_frame = tb.Frame(main_frame)
avg_frame.pack(fill=tk.X, pady=5)
# 保存平均利用率标签引用以便更新
self.avg_label = tb.Label(
avg_frame,
text=f"平均利用率: {data:.1f}%",
font=("Arial", 14),
bootstyle=INFO
)
self.avg_label.pack(side=tk.LEFT)
# 核心利用率容器
cores_frame = tb.Frame(main_frame)
cores_frame.pack(fill=tk.BOTH, expand=True, pady=10)
# 创建4列
for i in range(4):
cores_frame.columnconfigure(i, weight=1)
# 创建核心利用率进度条
self.core_bars = []
self.core_labels = [] # 保存核心利用率标签引用
for i, usage in enumerate(core_data):
frame = tb.Frame(cores_frame)
frame.grid(row=0, column=i, padx=10, pady=10, sticky="nsew")
# 核心标题
tb.Label(
frame,
text=f"{core_type} 核心 {i}",
font=("Arial", 12),
bootstyle=SECONDARY
).pack(pady=(0, 5))
# 进度条
progress_bar = EnhancedCircularProgressBar(
frame,
size=120,
thickness=12,
title="",
unit="%",
max_value=100
)
progress_bar.set_value(usage)
progress_bar.canvas.pack()
self.core_bars.append(progress_bar)
# 利用率标签
core_label = tb.Label(
frame,
text=f"{usage:.1f}%",
font=("Arial", 12),
bootstyle=INFO
)
core_label.pack(pady=(5, 0))
self.core_labels.append(core_label) # 保存标签引用
def update_values(self, avg_usage, core_usages):
"""更新详细窗口中的所有值"""
# 更新平均利用率标签
self.avg_label.config(text=f"平均利用率: {avg_usage:.1f}%")
# 更新核心利用率进度条和标签
for i, (bar, label) in enumerate(zip(self.core_bars, self.core_labels)):
if i < len(core_usages):
bar.set_value(core_usages[i])
label.config(text=f"{core_usages[i]:.1f}%")
# 队列监控组件类
class QueueMonitor:
def __init__(self, parent, server_ip="192.168.100.50", monitor_port=9997):
self.parent = parent
# 创建容器框架并设置固定尺寸
self.container = tb.Frame(parent)
self.container.pack(fill=tk.BOTH, expand=True)
self.container.config(width=900, height=600)
self.container.pack_propagate(False) # 阻止容器根据内容调整大小
# 创建内部框架,所有内容放在这个内部框架中
self.frame = tb.Frame(self.container)
self.frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 连接状态变量
self.connected = False
self.server_ip = server_ip
self.monitor_port = monitor_port
self.monitor_running = True
# 任务队列数据
self.cpu_queue = []
self.mlu_queue = []
self.current_cpu_task = ""
self.current_mlu_task = ""
self.cpu_eta = ""
self.mlu_eta = ""
self.report_time = "" # 新增:存储报告时间
# 创建UI
self.create_widgets()
# 启动状态监听线程
self.start_monitor_thread()
def create_widgets(self):
"""创建专业UI界面,优化布局"""
# 主容器
main_container = tb.Frame(self.frame, padding=(15, 15))
main_container.pack(fill=tk.BOTH, expand=True)
# 标题区域
header_frame = tb.Frame(main_container, padding=(10, 10))
header_frame.pack(fill=tk.X, pady=(0, 10))
tb.Label(
header_frame,
text="任务队列监控",
font=("Segoe UI", 16, "bold"),
).pack(side=tk.LEFT, padx=6)
right_status_frame = tb.Frame(header_frame)
right_status_frame.pack(side=tk.RIGHT)
# 报告时间标签
self.time_label = tb.Label(
right_status_frame,
text="报告时间: -",
bootstyle=INFO,
font=("Segoe UI", 10),
)
self.time_label.pack(side=tk.TOP, pady=(0, 5)) # 上方元素,底部留空
# 分隔线
separator = tb.Separator(
right_status_frame,
orient=tk.HORIZONTAL,
bootstyle=SECONDARY
)
separator.pack(fill=tk.X, pady=3) # 在时间和状态之间添加分隔线
# 连接状态指示器
self.status_indicator = tb.Label(
right_status_frame,
text="● 未连接",
bootstyle=DANGER,
font=("Segoe UI", 10, "bold"),
)
self.status_indicator.pack(side=tk.TOP) # 下方元素
# 控制面板
control_frame = tb.Frame(main_container)
control_frame.pack(fill=tk.X, pady=(0, 10))
# 服务器配置网格
config_grid = tb.Frame(control_frame)
config_grid.pack(fill=tk.X, padx=5, pady=5)
# 服务器地址
tb.Label(
config_grid,
text="服务器地址:",
font=("Segoe UI", 9),
).grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
self.ip_entry = tb.Entry(
config_grid,
width=22,
font=("Consolas", 9),
)
self.ip_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
self.ip_entry.insert(0, self.server_ip)
# 监控端口
tb.Label(
config_grid,
text="监控端口:",
font=("Segoe UI", 9),
).grid(row=0, column=2, padx=(15, 5), pady=5, sticky=tk.W)
self.monitor_entry = tb.Entry(
config_grid,
width=8,
font=("Consolas", 9),
)
self.monitor_entry.grid(row=0, column=3, padx=5, pady=5, sticky=tk.W)
self.monitor_entry.insert(0, str(self.monitor_port))
# 连接按钮
self.connect_btn = tb.Button(
config_grid,
text="连接监控",
command=self.connect_to_server,
bootstyle=(OUTLINE,DARK),
width=10
)
self.connect_btn.grid(row=0, column=4, padx=(15, 5))
# 队列状态区域
queue_frame = tb.Frame(main_container)
queue_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
# 创建网格布局 - 改为2列
queue_frame.columnconfigure(0, weight=1)
queue_frame.columnconfigure(1, weight=1)
queue_frame.rowconfigure(0, weight=1)
# CPU队列面板 - 常驻队列和调度队列两个子面板
cpu_frame = tb.LabelFrame(
queue_frame,
text="CPU队列状态",
padding=10,
)
cpu_frame.grid(row=0, column=0, padx=(0, 5), sticky="nsew")
# CPU队列内容 - 使用网格布局分为常驻队列和调度队列
cpu_grid = tb.Frame(cpu_frame)
cpu_grid.pack(fill=tk.BOTH, expand=True)
# 常驻队列部分
cpu_resident_frame = tb.LabelFrame(
cpu_grid,
text="常驻队列",
padding=5,
bootstyle="info"
)
cpu_resident_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")
# 常驻队列标题
tb.Label(
cpu_resident_frame,
text="当前任务:",
font=("Segoe UI", 9, "bold"),
).pack(anchor=tk.W, padx=5, pady=(0, 5))
# 常驻队列内容
self.cpu_resident_text = scrolledtext.ScrolledText(
cpu_resident_frame,
wrap=tk.WORD,
height=4,
font=("Consolas", 9),
state=tk.DISABLED
)
self.cpu_resident_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=(0, 5))
# 调度队列部分
cpu_schedule_frame = tb.LabelFrame(
cpu_grid,
text="调度队列",
padding=5,
bootstyle="warning"
)
cpu_schedule_frame.grid(row=1, column=0, padx=5, pady=5, sticky="nsew")
# 调度队列标题
tb.Label(
cpu_schedule_frame,
text="当前任务:",
font=("Segoe UI", 9, "bold"),
).pack(anchor=tk.W, padx=5, pady=(0, 5))
# 调度队列内容
self.cpu_schedule_text = scrolledtext.ScrolledText(
cpu_schedule_frame,
wrap=tk.WORD,
height=4,
font=("Consolas", 9),
state=tk.DISABLED
)
self.cpu_schedule_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=(0, 5))
# 设置网格权重
cpu_grid.rowconfigure(0, weight=1)
cpu_grid.rowconfigure(1, weight=1)
cpu_grid.columnconfigure(0, weight=1)
# MLU队列面板 - 同样改为常驻队列和调度队列两个子面板
mlu_frame = tb.LabelFrame(
queue_frame,
text="MLU队列状态",
padding=10,
)
mlu_frame.grid(row=0, column=1, padx=(5, 0), sticky="nsew")
# MLU队列内容 - 使用网格布局分为常驻队列和调度队列
mlu_grid = tb.Frame(mlu_frame)
mlu_grid.pack(fill=tk.BOTH, expand=True)
# 常驻队列部分
mlu_resident_frame = tb.LabelFrame(
mlu_grid,
text="常驻队列",
padding=5,
bootstyle="info"
)
mlu_resident_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")
# 常驻队列标题
tb.Label(
mlu_resident_frame,
text="当前任务:",
font=("Segoe UI", 9, "bold"),
).pack(anchor=tk.W, padx=5, pady=(0, 5))
# 常驻队列内容
self.mlu_resident_text = scrolledtext.ScrolledText(
mlu_resident_frame,
wrap=tk.WORD,
height=4,
font=("Consolas", 9),
state=tk.DISABLED
)
self.mlu_resident_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=(0, 5))
# 调度队列部分
mlu_schedule_frame = tb.LabelFrame(
mlu_grid,
text="调度队列",
padding=5,
bootstyle="warning"
)
mlu_schedule_frame.grid(row=1, column=0, padx=5, pady=5, sticky="nsew")
# 调度队列标题
tb.Label(
mlu_schedule_frame,
text="当前任务:",
font=("Segoe UI", 9, "bold"),
).pack(anchor=tk.W, padx=5, pady=(0, 5))
# 调度队列内容
self.mlu_schedule_text = scrolledtext.ScrolledText(
mlu_schedule_frame,
wrap=tk.WORD,
height=4,
font=("Consolas", 9),
state=tk.DISABLED
)
self.mlu_schedule_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=(0, 5))
# 设置网格权重
mlu_grid.rowconfigure(0, weight=1)
mlu_grid.rowconfigure(1, weight=1)
mlu_grid.columnconfigure(0, weight=1)
# 底部状态栏
status_frame = tb.Frame(self.frame)
status_frame.pack(fill=tk.X, side=tk.BOTTOM)
self.status_var = tk.StringVar()
self.status_var.set("就绪")
status_label = tb.Label(
status_frame,
textvariable=self.status_var,
font=("Segoe UI", 9),
)
status_label.pack(side=tk.LEFT, padx=15)
# 任务计数
self.task_count_var = tk.StringVar()
self.task_count_var.set("总任务: 0")
task_count_label = tb.Label(
status_frame,
textvariable=self.task_count_var,
font=("Segoe UI", 9),
)
task_count_label.pack(side=tk.RIGHT, padx=15)
def connect_to_server(self):
"""连接到服务器监控"""
try:
self.server_ip = self.ip_entry.get()
self.monitor_port = int(self.monitor_entry.get())
# 重启监控线程以使用新的配置
self.monitor_running = False
time.sleep(0.5) # 等待线程结束
self.start_monitor_thread()
self.connected = True
self.status_indicator.config(text="● 已连接", bootstyle=SUCCESS)
self.status_var.set(f"已连接到 {self.server_ip}:{self.monitor_port}")
self.update_status("监控连接成功")
except Exception as e:
self.update_status(f"连接失败: {str(e)}")
self.status_indicator.config(text="● 连接失败", bootstyle=DANGER)
def start_monitor_thread(self):
"""启动监控线程"""
self.monitor_running = True
threading.Thread(target=self.monitor_status, daemon=True).start()
self.update_status("监控线程已启动")
def monitor_status(self):
"""监听服务器状态"""
monitor_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
monitor_socket.bind(('0.0.0.0', self.monitor_port))
self.update_status(f"开始监听端口: {self.monitor_port}")
self.update_status(f"等待来自 {self.server_ip} 的数据...")
except Exception as e:
self.update_status(f"端口绑定失败: {str(e)}")
return
while self.monitor_running:
try:
data, addr = monitor_socket.recvfrom(4096)
if addr[0] != self.server_ip:
self.update_status(f"收到来自 {addr[0]} 的数据,已忽略")
continue
try:
status_str = data.decode('utf-8')
self.parse_queue_status(status_str)
self.update_status("收到队列状态更新")
self.last_update_var.set(f"更新: {datetime.now().strftime('%H:%M:%S')}")
except UnicodeDecodeError:
self.update_status("收到非文本数据,无法解码")
except Exception as e:
self.update_status(f"监听错误: {str(e)}")
time.sleep(1)
monitor_socket.close()
self.update_status("监控线程已停止")
def parse_queue_status(self, status_str):
"""解析队列状态字符串 - 更新解析逻辑以适应新格式"""
# 初始化队列数据
self.cpu_resident_queue = [] # CPU常驻队列
self.cpu_schedule_queue = [] # CPU调度队列
self.mlu_resident_queue = [] # MLU常驻队列
self.mlu_schedule_queue = [] # MLU调度队列
lines = status_str.split('\n')
if len(lines) < 8:
self.update_status("状态信息格式错误,无法解析")
return
# 解析队列状态报告时间
self.report_time = "未知时间"
for line in lines:
if "队列状态报告" in line:
# 尝试提取时间部分
# 行格式:队列状态报告 - 2023-03-02 14:30:46
parts = line.split(' - ')
if len(parts) > 1:
self.report_time = parts[1].strip()
break
# 更新报告时间标签
self.time_label.config(text=f"报告时间: {self.report_time}")
# 解析CPU队列
current_section = None
for line in lines:
line = line.strip()
# 检测队列类型
if "CPU常驻队列" in line:
current_section = "CPU_RESIDENT"
continue
elif "CPU调度队列" in line:
current_section = "CPU_SCHEDULE"
continue
elif "MLU常驻队列" in line:
current_section = "MLU_RESIDENT"
continue
elif "MLU调度队列" in line:
current_section = "MLU_SCHEDULE"
continue
elif "无任务" in line:
# 空队列标记
current_section = None
continue
# 解析任务行
if current_section and line.startswith("任务"):
# 提取任务信息
task_info = line.split(":", 1)[1].strip()
# 添加到相应队列
if current_section == "CPU_RESIDENT":
self.cpu_resident_queue.append(task_info)
elif current_section == "CPU_SCHEDULE":
self.cpu_schedule_queue.append(task_info)
elif current_section == "MLU_RESIDENT":
self.mlu_resident_queue.append(task_info)
elif current_section == "MLU_SCHEDULE":
self.mlu_schedule_queue.append(task_info)
# 更新UI
self.update_queue_display()
def update_queue_display(self):
"""更新队列显示 - 适配新的队列结构"""
# CPU常驻队列
self.cpu_resident_text.config(state=tk.NORMAL)
self.cpu_resident_text.delete(1.0, tk.END)
if self.cpu_resident_queue:
for task in self.cpu_resident_queue:
self.cpu_resident_text.insert(tk.END, f"• {task}\n")
else:
self.cpu_resident_text.insert(tk.END, "无任务")
self.cpu_resident_text.config(state=tk.DISABLED)
# CPU调度队列
self.cpu_schedule_text.config(state=tk.NORMAL)
self.cpu_schedule_text.delete(1.0, tk.END)
if self.cpu_schedule_queue:
for task in self.cpu_schedule_queue:
self.cpu_schedule_text.insert(tk.END, f"• {task}\n")
else:
self.cpu_schedule_text.insert(tk.END, "无任务")
self.cpu_schedule_text.config(state=tk.DISABLED)
# MLU常驻队列
self.mlu_resident_text.config(state=tk.NORMAL)
self.mlu_resident_text.delete(1.0, tk.END)
if self.mlu_resident_queue:
for task in self.mlu_resident_queue:
self.mlu_resident_text.insert(tk.END, f"• {task}\n")
else:
self.mlu_resident_text.insert(tk.END, "无任务")
self.mlu_resident_text.config(state=tk.DISABLED)
# MLU调度队列
self.mlu_schedule_text.config(state=tk.NORMAL)
self.mlu_schedule_text.delete(1.0, tk.END)
if self.mlu_schedule_queue:
for task in self.mlu_schedule_queue:
self.mlu_schedule_text.insert(tk.END, f"• {task}\n")
else:
self.mlu_schedule_text.insert(tk.END, "无任务")
self.mlu_schedule_text.config(state=tk.DISABLED)
# 更新任务计数
total_tasks = (len(self.cpu_resident_queue) + len(self.cpu_schedule_queue) +
len(self.mlu_resident_queue) + len(self.mlu_schedule_queue))
self.task_count_var.set(f"总任务: {total_tasks}")
def update_status(self, message):
"""更新状态日志"""
# 更新状态栏
self.status_var.set(message)
def stop(self):
"""停止监控"""
self.monitor_running = False
time.sleep(0.2) # 等待线程结束
# 主应用类
class SystemMonitorApp:
def __init__(self):
# 创建主窗口 - 使用darkly主题
self.root = tb.Window(themename="darkly", title="系统资源监控", size=(1650, 900))
self.root.minsize(1300, 800)
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
self.style.configure(
'Custom.TFrame',
background=self.style.colors.inputbg # 使用输入框背景色作为次级背景
)
# 设置样式
self.style = tb.Style()
# 创建主框架 - 使用网格布局
main_frame = tb.Frame(self.root, padding=10)
main_frame.pack(fill=tk.BOTH, expand=True)
# 配置网格权重
main_frame.columnconfigure(0, weight=1) # 左侧面板
main_frame.columnconfigure(1, weight=3) # 中间面板
main_frame.columnconfigure(2, weight=1) # 右侧面板
main_frame.rowconfigure(1, weight=1)
# 获取当前主题颜色
self.bg_color = self.style.colors.bg
self.secondary_bg = self.style.colors.selectbg # 使用更深一级的背景色
# 创建自定义样式
self.style.configure(
'Custom.TFrame',
background=self.style.colors.inputbg # 使用输入框背景色作为次级背景
)
self.style.configure(
'Custom.TButton',
background=self.style.colors.inputbg, # 使用次级背景
bordercolor=self.style.colors.border, # 边框颜色
darkcolor=self.style.colors.inputbg, # 深色状态颜色
lightcolor=self.style.colors.inputbg # 浅色状态颜色
)
self.style.configure(
'Custom.TLabel',
background=self.style.colors.inputbg, # 使用次级背景
foreground=self.style.colors.fg # 前景色(文本颜色)使用主题的前景色
)
# ========== 顶部控制栏 ==========
control_bar = tb.Frame(main_frame, padding=(10, 5))
control_bar.grid(row=0, column=0, columnspan=3, sticky="ew", pady=(0, 10))
# 采样频率滑块
tb.Label(control_bar, text="采样频率:", bootstyle=PRIMARY).pack(side=tk.LEFT, padx=(20, 5))
self.sampling_rate = tk.IntVar(value=1)
tb.Scale(
control_bar,
from_=0.5,
to=5,
length=120,
orient=tk.HORIZONTAL,
variable=self.sampling_rate,
bootstyle=PRIMARY
).pack(side=tk.LEFT, padx=5)
# 动画速度滑块
tb.Label(control_bar, text="动画速度:", bootstyle=PRIMARY).pack(side=tk.LEFT, padx=(20, 5))
self.animation_speed = tk.DoubleVar(value=1.0)
tb.Scale(
control_bar,
from_=0.5,
to=3.0,
length=120,
orient=tk.HORIZONTAL,
variable=self.animation_speed,
bootstyle=PRIMARY
).pack(side=tk.LEFT, padx=5)
# 控制按钮
tb.Button(
control_bar,
text="启动",
bootstyle=(OUTLINE, PRIMARY),
width=8,
command=self.start_monitoring
).pack(side=tk.LEFT, padx=5)
tb.Button(
control_bar,
text="暂停",
bootstyle=(OUTLINE, PRIMARY),
width=8,
command=self.stop_monitoring
).pack(side=tk.LEFT, padx=5)
tb.Button(
control_bar,
text="重置",
bootstyle=(OUTLINE, PRIMARY),
width=8,
command=self.reset_all
).pack(side=tk.LEFT, padx=5)
# 状态指示器
self.status_indicator = tb.Label(
control_bar,
text="● 等待连接",
bootstyle=(WARNING, INVERSE),
font=("Arial", 10),
padding=(10, 0)
)
self.status_indicator.pack(side=tk.RIGHT, padx=(10, 0))
# ========== 左侧设置面板 - 使用更深背景色 ==========
settings_frame = tb.Frame(
main_frame,
padding=10,
style='Custom.TFrame'
)
settings_frame.grid(row=1, column=0, sticky="nsew", padx=(0, 10), pady=(0, 10))
# 添加标题
tb.Label(
settings_frame,
text="监控设置",
bootstyle=PRIMARY,
font=("Arial", 10, "bold"),
style='Custom.TLabel' # 应用自定义标签样式
).pack(anchor=tk.W, pady=(0, 10))
# 信息类型选择 - 四个垂直排列的按钮
tb.Label(
settings_frame,
text="信息类型:",
bootstyle=PRIMARY
).pack(anchor=tk.W, pady=(0, 5))
# 创建按钮容器 - 使用垂直排列
button_container = tb.Frame(settings_frame, style='Custom.TFrame')
button_container.pack(fill=tk.X, pady=(0, 10))
# 四个独立按钮 - 垂直排列
self.info_buttons = {}
info_types = ["总体信息", "MLU信息", "CPU信息", "队列信息"]
button_styles = [PRIMARY, PRIMARY, PRIMARY, PRIMARY]
for i, (info_type, style) in enumerate(zip(info_types, button_styles)):
btn = tb.Button(
button_container,
text=info_type,
bootstyle=(OUTLINE,DARK),
width=15,
command=lambda t=info_type: self.set_info_type(t)
)
# 存储按钮的样式信息
btn.pack(side=tk.TOP, pady=3, fill=tk.X)
self.info_buttons[info_type] = btn
# 默认选中总体信息
self.info_type_var = tk.StringVar(value="总体信息")
self.update_button_style("总体信息")
# 分隔线
tb.Separator(settings_frame, bootstyle=SECONDARY).pack(fill=tk.X, pady=10)
# 操作按钮框架 - 垂直排列
button_frame = tb.Frame(settings_frame)
button_frame.pack(fill=tk.X, pady=(10, 5))
# ========== 中间监控面板 ==========
self.monitor_frame = tb.Frame(main_frame, bootstyle="default")
self.monitor_frame.grid(row=1, column=1, sticky="nsew", pady=(0, 10))
# 创建卡片容器 - 固定尺寸
self.card_container = tb.Frame(self.monitor_frame)
self.card_container.pack(fill=tk.BOTH, expand=True)
self.card_container.config(width=1100, height=700)
self.card_container.pack_propagate(False)
# 创建资源监控卡片
self.resource_card = tb.Frame(self.card_container)
self.resource_card.pack(fill=tk.BOTH, expand=True)
# 标题
tb.Label(
self.resource_card,
text="系统资源实时监控",
font=("Arial", 16, "bold"),
bootstyle=PRIMARY
).pack(pady=(0, 15))
# 创建进度条容器框架
progress_container = tb.Frame(self.resource_card)
progress_container.pack(fill=tk.BOTH, expand=True, pady=5)
# 创建监控指标配置
monitor_config = [
{"title": "CPU温度", "unit": "°C", "max_value": 100, "thickness": 18},
{"title": "功耗", "unit": "W", "max_value": 15, "thickness": 18},
{"title": "内存使用", "unit": "%", "max_value": 100, "thickness": 18},
{"title": "网络带宽", "unit": "Mbps", "max_value": 1000, "thickness": 18},
{"title": "MLU利用率", "unit": "%", "max_value": 100, "thickness": 18},
{"title": "CPU利用率", "unit": "%", "max_value": 100, "thickness": 18}
]
# 使用网格布局排列进度条
self.progress_bars = []
for i, config in enumerate(monitor_config):
frame = tb.Frame(progress_container)
frame.grid(row=i//3, column=i%3, padx=15, pady=15, sticky="nsew")
# 创建增强型进度条
progress_bar = EnhancedCircularProgressBar(
frame,
size=220,
thickness=config["thickness"],
title=config["title"],
unit=config["unit"],
max_value=config["max_value"]
)
self.progress_bars.append(progress_bar)
progress_bar.canvas.pack(fill=tk.BOTH, expand=True)
# 设置网格列权重
for i in range(3):
progress_container.columnconfigure(i, weight=1)
for i in range(2):
progress_container.rowconfigure(i, weight=1)
# 创建队列监控卡片
self.queue_card = QueueMonitor(self.card_container)
self.queue_card.container.pack_forget() # 初始隐藏
# 默认显示资源监控卡片
self.resource_card.pack(fill=tk.BOTH, expand=True)
# ========== 右侧信息面板 - 使用更深背景色 ==========
info_frame = tb.Frame(
main_frame,
padding=10,
style='Custom.TFrame'
)
info_frame.grid(row=1, column=2, sticky="nsew", padx=(10, 0), pady=(0, 10))
# 添加标题
tb.Label(
info_frame,
text="系统信息",
bootstyle=PRIMARY,
font=("Arial", 10, "bold"),
style='Custom.TLabel' # 应用自定义标签样式
).pack(anchor=tk.W, pady=(0, 10))
# 系统信息标签 - 调整标签宽度
info_labels = [
("设备型号:", "MLU220"),
("操作系统:", "Ubuntu 20.04 LTS"),
("处理器:", "4核ARM Cortex-A53 CPU"),
("内存总量:", "4GB LPDDR4x"),
("MLU数量:", "4"),
("网络接口:", "PCIe 3.0×4接口")
]
for label, value in info_labels:
frame = tb.Frame(info_frame, style='Custom.TFrame')
frame.pack(fill=tk.X, pady=3)
# 增加标签宽度,确保显示完整
tb.Label(frame, text=label, width=12, anchor=tk.W, bootstyle=PRIMARY).pack(side=tk.LEFT)
tb.Label(frame, text=value, bootstyle=INFO, anchor=tk.W).pack(side=tk.LEFT, fill=tk.X, expand=True)
# 分隔线
tb.Separator(info_frame, bootstyle=SECONDARY).pack(fill=tk.X, pady=10)
# 实时状态
tb.Label(info_frame, text="实时状态", bootstyle=PRIMARY).pack(anchor=tk.W, pady=(0, 5))
self.realtime_labels = {}
status_items = [
("CPU温度", "cpu_temp", "°C"),
("功耗", "power", "W"),
("内存使用", "memory", "%"),
("网络带宽", "bandwidth", "Mbps"),
("MLU利用率", "mlu_usage", "%"),
("CPU利用率", "cpu_usage", "%")
]
for name, key, unit in status_items:
frame = tb.Frame(info_frame, style='Custom.TFrame')
frame.pack(fill=tk.X, pady=2)
# 增加标签宽度
tb.Label(frame, text=name, width=14, anchor=tk.W, bootstyle=PRIMARY).pack(side=tk.LEFT)
value_label = tb.Label(frame, text="0.0", width=10, anchor=tk.W, bootstyle=INFO)
value_label.pack(side=tk.LEFT)
tb.Label(frame, text=unit, bootstyle=PRIMARY).pack(side=tk.LEFT)
self.realtime_labels[key] = value_label
# 核心利用率框架
self.core_usage_frame = tb.LabelFrame(
info_frame,
text="核心利用率",
bootstyle="info",
padding=5
)
self.core_usage_frame.pack(fill=tk.X, pady=10)
# 创建标签用于显示核心利用率 - 调整布局
self.core_labels = {}
for i in range(4): # 4个核心
frame = tb.Frame(self.core_usage_frame)
frame.pack(fill=tk.X, pady=2)
# 核心标题 - 增加宽度
tb.Label(frame, text=f"核心 {i}:", width=10, anchor=tk.W, bootstyle=PRIMARY).pack(side=tk.LEFT)
# CPU核心标签
cpu_frame = tb.Frame(frame)
cpu_frame.pack(side=tk.LEFT, padx=(0, 5))
tb.Label(cpu_frame, text="CPU:", anchor=tk.W, bootstyle=PRIMARY).pack(side=tk.LEFT)
cpu_label = tb.Label(cpu_frame, text="0.0%", width=6, anchor=tk.W, bootstyle=INFO)
cpu_label.pack(side=tk.LEFT)
self.core_labels[f"cpu_core_{i}"] = cpu_label
# MLU核心标签
mlu_frame = tb.Frame(frame)
mlu_frame.pack(side=tk.LEFT)
tb.Label(mlu_frame, text="MLU:", anchor=tk.W, bootstyle=PRIMARY).pack(side=tk.LEFT)
mlu_label = tb.Label(mlu_frame, text="0.0%", width=6, anchor=tk.W, bootstyle=INFO)
mlu_label.pack(side=tk.LEFT)
self.core_labels[f"mlu_core_{i}"] = mlu_label
# ========== 状态栏 ==========
self.status = tb.Label(
self.root,
text="系统准备就绪 | 当前设备: MLU220 | 信息类型: 总体信息",
bootstyle=(SECONDARY, INVERSE),
anchor=tk.CENTER
)
self.status.pack(side=tk.BOTTOM, fill=tk.X)
# 监控控制变量
self.monitoring_active = False
self.monitoring_thread = None
# 创建数据接收器
self.data_receiver = MLUDataReceiver(self.data_received)
# 存储详细窗口引用
self.detail_window = None
self.last_data = None
# 初始状态
self.status_indicator.config(text="● 等待数据", bootstyle=(WARNING, INVERSE))
# 自动启动监控和连接队列监控
self.root.after(1000, self.auto_start)
self.root.mainloop()
def auto_start(self):
"""自动启动监控和连接队列监控"""
# 自动启动监控
self.start_monitoring()
# 自动连接队列监控
if hasattr(self, 'queue_card'):
self.queue_card.connect_to_server()
def update_button_style(self, active_type):
"""更新按钮样式以显示当前选中的信息类型"""
for info_type, button in self.info_buttons.items():
if info_type == active_type:
# 当前选中的按钮使用实心样式
button.configure(bootstyle=PRIMARY)
else:
# 其他按钮使用轮廓样式+自定义背景
button.configure(
bootstyle=(OUTLINE, PRIMARY),
style='Custom.TButton' # 应用自定义按钮样式
)
def set_info_type(self, info_type):
"""设置信息类型并更新按钮样式"""
self.info_type_var.set(info_type)
self.update_button_style(info_type)
self.info_type_changed()
def info_type_changed(self):
"""当信息类型变更时的处理"""
info_type = self.info_type_var.get()
self.status.config(text=f"已切换至: {info_type}")
# 关闭已有的详细窗口
if self.detail_window and self.detail_window.window.winfo_exists():
self.detail_window.window.destroy()
self.detail_window = None
# 根据选择的信息类型切换中间面板
if info_type == "队列信息":
# 隐藏资源监控卡片,显示队列监控卡片
self.resource_card.pack_forget()
self.queue_card.container.pack(fill=tk.BOTH, expand=True)
else:
# 隐藏队列监控卡片,显示资源监控卡片
self.queue_card.container.pack_forget()
self.resource_card.pack(fill=tk.BOTH, expand=True)
# 根据选择的信息类型显示详细窗口
if info_type == "MLU信息" and self.last_data:
self.show_detail_window("MLU核心利用率详情",
self.last_data['mlu_usage'],
self.last_data['mlu_cores'],
"MLU")
elif info_type == "CPU信息" and self.last_data:
self.show_detail_window("CPU核心利用率详情",
self.last_data['cpu_usage'],
self.last_data['cpu_cores'],
"CPU")
def show_detail_window(self, title, avg_usage, core_usages, core_type):
"""显示详细核心利用率窗口"""
# 关闭已有的详细窗口
if self.detail_window and self.detail_window.window.winfo_exists():
self.detail_window.window.destroy()
# 创建新窗口
self.detail_window = DetailWindow(self.root, title, avg_usage, core_usages, core_type)
def data_received(self, data):
"""从MLUDataReceiver接收数据的回调函数"""
if not self.monitoring_active:
return
# 更新状态指示器
if data['status'] == "在线":
self.status_indicator.config(text="● 已连接", bootstyle=(SUCCESS, INVERSE))
else:
self.status_indicator.config(text="● 设备离线", bootstyle=(DANGER, INVERSE))
# 保存最新数据
self.last_data = data
# 使用after安全更新UI
self.root.after(0, self.update_ui, data)
def start_monitoring(self):
"""启动资源监控"""
if self.monitoring_active:
return
self.status.config(text="启动系统资源监控...")
self.monitoring_active = True
self.status_indicator.config(text="● 接收数据中...", bootstyle=(INFO, INVERSE))
def stop_monitoring(self):
"""停止资源监控"""
self.monitoring_active = False
self.status.config(text="监控已暂停")
self.status_indicator.config(text="● 监控暂停", bootstyle=(WARNING, INVERSE))
def reset_all(self):
"""重置所有监控指标"""
for bar in self.progress_bars:
bar.set_value(0)
self.status.config(text="所有监控指标已重置")
def change_theme(self):
"""更改应用主题"""
theme = self.theme_var.get()
# 正确切换主题方法
self.root.style.theme_use(theme)
self.status.config(text=f"主题已切换为: {theme.capitalize()}")
# 更新所有进度条的颜色
style = tb.Style()
for bar in self.progress_bars:
bar.text_color = style.colors.fg
bar.bg_color = style.colors.bg
bar.secondary_bg = style.colors.inputbg
bar.canvas.config(bg=bar.bg_color)
bar.canvas.itemconfig(bar.text_id, fill=bar.text_color)
bar.canvas.itemconfig(bar.title_id, fill=bar.text_color)
bar.draw_background()
bar.update_display()
def update_ui(self, data):
"""安全更新UI组件(在主线程执行)"""
# 更新进度条的动画速度
speed = self.animation_speed.get()
for bar in self.progress_bars:
bar.animation_speed = speed
# 更新进度条
self.progress_bars[0].set_value(data['cpu_temp'])
self.progress_bars[1].set_value(data['power'])
self.progress_bars[2].set_value(data['mem_usage'])
self.progress_bars[3].set_value(data['bandwidth'])
self.progress_bars[4].set_value(data['mlu_usage'])
self.progress_bars[5].set_value(data['cpu_usage'])
# 更新实时状态标签
self.realtime_labels["cpu_temp"].config(text=f"{data['cpu_temp']:.1f}")
self.realtime_labels["power"].config(text=f"{data['power']:.1f}")
self.realtime_labels["memory"].config(text=f"{data['mem_usage']:.1f}")
self.realtime_labels["bandwidth"].config(text=f"{data['bandwidth']:.1f}")
self.realtime_labels["mlu_usage"].config(text=f"{data['mlu_usage']:.1f}")
self.realtime_labels["cpu_usage"].config(text=f"{data['cpu_usage']:.1f}")
# 更新核心利用率标签
for i in range(4):
self.core_labels[f"cpu_core_{i}"].config(text=f"{data['cpu_cores'][i]:.1f}%")
self.core_labels[f"mlu_core_{i}"].config(text=f"{data['mlu_cores'][i]:.1f}%")
# 更新状态栏
info_type = self.info_type_var.get()
status_text = (
f"当前设备: MLU220 | "
f"信息类型: {info_type} | "
f"状态: {data['status']} | "
f"CPU: {data['cpu_usage']:.1f}% | "
f"温度: {data['cpu_temp']:.1f}°C | "
f"内存: {data['mem_usage']:.1f}% | "
f"MLU: {data['mlu_usage']:.1f}%"
)
self.status.config(text=status_text)
# 如果详细窗口存在且是当前选择的信息类型,更新详细窗口
current_type = self.info_type_var.get()
if self.detail_window and self.detail_window.window.winfo_exists():
if current_type == "MLU信息":
self.detail_window.update_values(
data['mlu_usage'],
data['mlu_cores']
)
elif current_type == "CPU信息":
self.detail_window.update_values(
data['cpu_usage'],
data['cpu_cores']
)
def on_closing(self):
"""窗口关闭时的清理操作"""
if hasattr(self, 'data_receiver'):
self.data_receiver.stop()
# 停止队列监控
if hasattr(self, 'queue_card'):
self.queue_card.stop()
self.root.destroy()
if __name__ == "__main__":
app = SystemMonitorApp()修改之后出现了错误,帮我改修,写出完整的修改代码