import tkinter as tk
from icmplib import ping
from datetime import datetime
from collections import deque
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import configparser
import matplotlib.dates as mdates
import threading
UPDATETIME = 20
INI_FILE = 'hosts.ini'
COLUMN = 2
GRAHP_WIDTH = 600
GRAHP_HEIGHT = 300
GUI_GEOMETRY = "1220x650"
PING_COUNT = 2
PING_INTERVAL = 2
DATA_LIMIT = 3000
class Host:
def __init__(self, ip, name, width_pixels, height_pixels, log_callback):
self.ip = ip
self.name = name
self.ping_data = deque(maxlen=DATA_LIMIT)
self.time = deque(maxlen=DATA_LIMIT)
self.log_callback = log_callback
dpi = 100
width_inches = width_pixels / dpi
height_inches = height_pixels / dpi
self.fig, self.ax = plt.subplots(figsize=(width_inches, height_inches), dpi=dpi)
self.line, = self.ax.plot([], [], label=f'{ip} {name} ms', color='darkgreen', linestyle='-', linewidth=2)
self.ax.legend()
self.ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
self.ax.xaxis.set_major_locator(mdates.MinuteLocator(interval=30))
self.fig.autofmt_xdate()
def ping_host(self, ip, count=PING_COUNT, interval=PING_INTERVAL):
return ping(ip, count=count, interval=interval)
def update_ping(self):
try:
response = self.ping_host(self.ip)
current_time = datetime.now()
self.time.append(current_time)
if response.is_alive:
self.ping_data.append(response.avg_rtt)
else:
self.ping_data.append(0)
self.log_callback(f"{current_time.year}.{current_time.month}.{current_time.day} {current_time.hour}:{current_time.minute} : {self.name} ({self.ip}) недоступен\n")
self.line.set_data(self.time, self.ping_data)
self.ax.relim()
self.ax.autoscale_view()
self.fig.tight_layout()
self.canvas.draw()
except Exception as e:
print(f"Error pinging {self.ip}: {e}")
class App:
def __init__(self, GUI, ini_file):
self.gui = GUI
self.gui.title("Монитор Доступности оборудования")
self.gui.geometry(GUI_GEOMETRY)
self.hosts = self.load_hosts_from_ini(ini_file)
self.log_text = tk.Text(self.gui, height=10, width=80, wrap='none', state='disabled')
self.log_text.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
self.log_scrollbar = tk.Scrollbar(self.gui, orient="vertical", command=self.log_text.yview)
self.log_scrollbar.grid(row=0, column=1, sticky="ns")
self.log_text['yscrollcommand'] = self.log_scrollbar.set
self.canvas = tk.Canvas(self.gui)
self.scrollbar = tk.Scrollbar(self.gui, orient="vertical", command=self.canvas.yview)
self.scrollable_frame = tk.Frame(self.canvas)
self.scrollable_frame.bind(
"<Configure>",
lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
)
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
for i, host in enumerate(self.hosts):
canvas = FigureCanvasTkAgg(host.fig, master=self.scrollable_frame)
canvas.get_tk_widget().grid(row=i//COLUMN, column=i%COLUMN, padx=5, pady=5)
host.canvas = canvas
self.canvas.grid(row=1, column=0, sticky="nsew")
self.scrollbar.grid(row=1, column=1, sticky="ns")
self.gui.grid_rowconfigure(1, weight=1)
self.gui.grid_columnconfigure(0, weight=1)
self.start_ping()
def load_hosts_from_ini(self, ini_file):
config = configparser.ConfigParser()
config.read(ini_file)
return [Host(ip, hostname, width_pixels=GRAHP_WIDTH, height_pixels=GRAHP_HEIGHT, log_callback=self.log_message) for section in config.sections() for hostname, ip in config.items(section)]
def log_message(self, message):
self.log_text.config(state='normal')
self.log_text.insert("1.0", message)
self.log_text.config(state='disabled')
def start_ping(self):
for host in self.hosts:
thread = threading.Thread(target=host.update_ping)
thread.start()
self.gui.after(UPDATETIME * 1000, self.start_ping)
GUI = tk.Tk()
app = App(GUI, INI_FILE)
GUI.mainloop()