Add advanced port monitoring and execution progress tracking
- Implement real-time COM port state monitoring with automatic reconnection - Add connection indicator with dynamic status updates - Create progress bar and timer for file command execution - Enhance execution control with detailed progress tracking - Implement thread-safe port monitoring and execution mechanisms - Improve error handling and user feedback during command execution
This commit is contained in:
251
ComConfigCopy.py
251
ComConfigCopy.py
@@ -21,6 +21,7 @@ from tkinter import (
|
||||
END,
|
||||
BOTH,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
X,
|
||||
W,
|
||||
filedialog,
|
||||
@@ -765,6 +766,15 @@ class SerialAppGUI(tk.Tk):
|
||||
self.execution_paused = False
|
||||
self.execution_stop = False
|
||||
|
||||
# Добавляем атрибуты для таймера и прогресса
|
||||
self.start_time = 0
|
||||
self.elapsed_time = 0
|
||||
self.timer_running = False
|
||||
|
||||
# Добавляем атрибут для отслеживания состояния COM-порта
|
||||
self.port_monitor_thread = None
|
||||
self.port_monitoring = False
|
||||
|
||||
# Инициализация проверки обновлений
|
||||
self.update_checker = UpdateChecker(
|
||||
VERSION,
|
||||
@@ -966,23 +976,71 @@ class SerialAppGUI(tk.Tk):
|
||||
self.connection = create_connection(self.settings)
|
||||
if self.connection:
|
||||
self.interactive_text.append_info("[INFO] Подключение установлено.")
|
||||
self.update_status_bar() # Обновляем статус бар
|
||||
# Запускаем мониторинг состояния порта
|
||||
self.start_port_monitoring()
|
||||
self.update_status_bar()
|
||||
else:
|
||||
self.interactive_text.append_error("[ERROR] Не удалось установить соединение.")
|
||||
self.update_status_bar()
|
||||
|
||||
# Отключение от устройства
|
||||
def disconnect_device(self):
|
||||
if self.connection:
|
||||
try:
|
||||
# Останавливаем мониторинг перед закрытием соединения
|
||||
self.stop_port_monitoring()
|
||||
self.connection.close()
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self.connection = None
|
||||
self.interactive_text.append_info("[INFO] Соединение закрыто.")
|
||||
self.update_status_bar() # Обновляем статус бар
|
||||
self.update_status_bar()
|
||||
else:
|
||||
messagebox.showinfo("Информация", "Соединение не установлено.")
|
||||
|
||||
def start_port_monitoring(self):
|
||||
"""Запуск мониторинга состояния COM-порта"""
|
||||
if not self.port_monitor_thread or not self.port_monitor_thread.is_alive():
|
||||
self.port_monitoring = True
|
||||
self.port_monitor_thread = threading.Thread(target=self.monitor_port_state, daemon=True)
|
||||
self.port_monitor_thread.start()
|
||||
|
||||
def stop_port_monitoring(self):
|
||||
"""Остановка мониторинга состояния COM-порта"""
|
||||
self.port_monitoring = False
|
||||
if self.port_monitor_thread and self.port_monitor_thread.is_alive():
|
||||
self.port_monitor_thread.join(timeout=1.0)
|
||||
|
||||
def monitor_port_state(self):
|
||||
"""Мониторинг состояния COM-порта"""
|
||||
while self.port_monitoring and self.connection:
|
||||
try:
|
||||
# Проверяем, что порт всё ещё открыт и отвечает
|
||||
if not self.connection.is_open:
|
||||
self.connection.open()
|
||||
# Если порт отвечает, обновляем статус
|
||||
self.after(0, self.update_connection_indicator, True)
|
||||
except (SerialException, OSError):
|
||||
# Если возникла ошибка, помечаем порт как отключенный
|
||||
self.after(0, self.update_connection_indicator, False)
|
||||
# Пытаемся переподключиться
|
||||
try:
|
||||
if self.connection:
|
||||
self.connection.close()
|
||||
except:
|
||||
pass
|
||||
self.connection = None
|
||||
break
|
||||
time.sleep(1) # Проверяем состояние каждую секунду
|
||||
|
||||
def update_connection_indicator(self, is_connected):
|
||||
"""Обновление индикатора подключения COM-порта"""
|
||||
if is_connected:
|
||||
self.connection_indicator.configure(text="⬤", foreground='green')
|
||||
else:
|
||||
self.connection_indicator.configure(text="⬤", foreground='red')
|
||||
|
||||
# Отправка команды
|
||||
def send_command(self):
|
||||
if not self.connection:
|
||||
@@ -1049,6 +1107,23 @@ class SerialAppGUI(tk.Tk):
|
||||
self.stop_button = ttk.Button(control_frame, text="⏹ Остановить", command=self.stop_execution, state="disabled")
|
||||
self.stop_button.pack(side=LEFT, padx=5)
|
||||
|
||||
# Создаем фрейм для индикатора прогресса и таймера
|
||||
progress_frame = ttk.Frame(frame)
|
||||
progress_frame.pack(fill=X, pady=5, padx=5)
|
||||
|
||||
# Добавляем индикатор прогресса
|
||||
progress_label_frame = ttk.Frame(progress_frame)
|
||||
progress_label_frame.pack(fill=X)
|
||||
|
||||
self.progress_label = ttk.Label(progress_label_frame, text="Прогресс: 0/0 команд")
|
||||
self.progress_label.pack(side=LEFT, padx=5)
|
||||
|
||||
self.timer_label = ttk.Label(progress_label_frame, text="Время: 00:00:00")
|
||||
self.timer_label.pack(side=RIGHT, padx=5)
|
||||
|
||||
self.progress_bar = ttk.Progressbar(progress_frame, mode='determinate')
|
||||
self.progress_bar.pack(fill=X, pady=5)
|
||||
|
||||
# Используем новый TerminalWidget вместо CustomText
|
||||
self.file_exec_text = TerminalWidget(frame, height=15)
|
||||
self.file_exec_text.pack(fill=BOTH, expand=True, padx=5, pady=5)
|
||||
@@ -1079,6 +1154,9 @@ class SerialAppGUI(tk.Tk):
|
||||
return
|
||||
if not self.connection:
|
||||
self.connection = create_connection(self.settings)
|
||||
if self.connection:
|
||||
# Запускаем мониторинг при новом подключении
|
||||
self.start_port_monitoring()
|
||||
if not self.connection:
|
||||
self.append_file_exec_text("[ERROR] Не удалось установить соединение.\n")
|
||||
return
|
||||
@@ -1091,24 +1169,38 @@ class SerialAppGUI(tk.Tk):
|
||||
self.execution_paused = False
|
||||
self.execution_stop = False
|
||||
|
||||
# Запускаем выполнение команд
|
||||
self.execute_next_command()
|
||||
# Инициализируем прогресс бар
|
||||
self.progress_bar['value'] = 0
|
||||
self.update_progress()
|
||||
|
||||
# Запускаем таймер
|
||||
self.start_time = time.time()
|
||||
self.elapsed_time = 0
|
||||
self.timer_running = True
|
||||
self.update_timer()
|
||||
|
||||
# Запускаем выполнение команд в отдельном потоке
|
||||
threading.Thread(target=self.command_execution_thread, daemon=True).start()
|
||||
|
||||
except Exception as e:
|
||||
self.append_file_exec_text(f"[ERROR] Ошибка при чтении файла: {str(e)}\n")
|
||||
self.reset_execution_buttons()
|
||||
|
||||
def execute_next_command(self):
|
||||
def command_execution_thread(self):
|
||||
"""Отдельный поток для выполнения команд"""
|
||||
copy_mode = self.settings.get("copy_mode", "line")
|
||||
block_size = self.settings.get("block_size", 15)
|
||||
|
||||
if copy_mode == "line":
|
||||
# Построчный режим
|
||||
while self.current_command_index < len(self.commands):
|
||||
if self.execution_stop:
|
||||
self.reset_execution_buttons()
|
||||
self.append_file_exec_text("[INFO] Выполнение остановлено.\n")
|
||||
return
|
||||
break
|
||||
|
||||
if self.execution_paused:
|
||||
self.after(1000, self.execute_next_command)
|
||||
return
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
if self.current_command_index < len(self.commands):
|
||||
cmd = self.commands[self.current_command_index]
|
||||
try:
|
||||
success, response = send_command_and_process_response(
|
||||
@@ -1122,24 +1214,99 @@ class SerialAppGUI(tk.Tk):
|
||||
is_gui=True
|
||||
)
|
||||
|
||||
if not success:
|
||||
self.append_file_exec_text(f"[ERROR] Не удалось выполнить команду: {cmd}\n")
|
||||
|
||||
self.current_command_index += 1
|
||||
# Планируем выполнение следующей команды
|
||||
self.after(1000, self.execute_next_command)
|
||||
self.after(0, self.update_progress)
|
||||
time.sleep(1) # Задержка между командами
|
||||
|
||||
except Exception as e:
|
||||
self.append_file_exec_text(f"[ERROR] Ошибка при выполнении команды: {str(e)}\n")
|
||||
self.reset_execution_buttons()
|
||||
break
|
||||
else:
|
||||
# Блочный режим
|
||||
blocks = generate_command_blocks(self.commands, block_size)
|
||||
total_blocks = len(blocks)
|
||||
current_block = 0
|
||||
|
||||
while current_block < total_blocks:
|
||||
if self.execution_stop:
|
||||
break
|
||||
|
||||
if self.execution_paused:
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
block = blocks[current_block]
|
||||
try:
|
||||
self.append_file_exec_text(f"[CMD] Отправка блока команд:\n{block}\n")
|
||||
success, response = send_command_and_process_response(
|
||||
self.connection,
|
||||
block,
|
||||
self.settings.get("timeout", 10),
|
||||
max_attempts=3,
|
||||
log_callback=self.append_file_exec_text,
|
||||
login=self.settings.get("login"),
|
||||
password=self.settings.get("password"),
|
||||
is_gui=True
|
||||
)
|
||||
|
||||
if not success or (response and '^' in response):
|
||||
self.append_file_exec_text("[WARNING] Ошибка при выполнении блока команд. Отправляю команды по отдельности...\n")
|
||||
# Отправляем команды блока по отдельности
|
||||
for cmd in block.splitlines():
|
||||
if self.execution_stop:
|
||||
break
|
||||
if cmd.strip():
|
||||
success, resp = send_command_and_process_response(
|
||||
self.connection,
|
||||
cmd,
|
||||
self.settings.get("timeout", 10),
|
||||
max_attempts=3,
|
||||
log_callback=self.append_file_exec_text,
|
||||
login=self.settings.get("login"),
|
||||
password=self.settings.get("password"),
|
||||
is_gui=True
|
||||
)
|
||||
if not success:
|
||||
self.append_file_exec_text(f"[ERROR] Не удалось выполнить команду: {cmd}\n")
|
||||
|
||||
# Обновляем прогресс на основе количества выполненных блоков
|
||||
current_block += 1
|
||||
self.current_command_index = (current_block * 100) // total_blocks
|
||||
self.after(0, self.update_progress)
|
||||
time.sleep(1) # Задержка между блоками
|
||||
|
||||
except Exception as e:
|
||||
self.append_file_exec_text(f"[ERROR] Ошибка при выполнении блока команд: {str(e)}\n")
|
||||
break
|
||||
|
||||
# Завершение выполнения
|
||||
self.after(0, self.execution_completed)
|
||||
|
||||
def execution_completed(self):
|
||||
"""Обработка завершения выполнения в главном потоке"""
|
||||
if self.execution_stop:
|
||||
self.append_file_exec_text("[INFO] Выполнение остановлено.\n")
|
||||
else:
|
||||
self.append_file_exec_text("[INFO] Выполнение завершено.\n")
|
||||
self.reset_execution_buttons()
|
||||
|
||||
def start_execution(self):
|
||||
if not self.execution_thread or not self.execution_thread.is_alive():
|
||||
if not hasattr(self, 'commands') or not self.commands:
|
||||
self.execution_thread = threading.Thread(target=self.execute_file_commands, daemon=True)
|
||||
self.execution_thread.start()
|
||||
else:
|
||||
# Если команды уже загружены, просто возобновляем выполнение
|
||||
self.execution_paused = False
|
||||
self.execution_stop = False
|
||||
self.timer_running = True
|
||||
threading.Thread(target=self.command_execution_thread, daemon=True).start()
|
||||
|
||||
self.start_button.config(state="disabled")
|
||||
self.pause_button.config(state="normal")
|
||||
self.stop_button.config(state="normal")
|
||||
self.execution_thread = threading.Thread(target=self.execute_file_commands, daemon=True)
|
||||
self.execution_thread.start()
|
||||
|
||||
def pause_execution(self):
|
||||
if not self.execution_paused:
|
||||
@@ -1154,7 +1321,24 @@ class SerialAppGUI(tk.Tk):
|
||||
def stop_execution(self):
|
||||
self.execution_stop = True
|
||||
self.execution_paused = False
|
||||
self.append_file_exec_text("[INFO] Остановка выполнения...\n")
|
||||
self.timer_running = False
|
||||
|
||||
# Отключаемся от COM-порта
|
||||
if self.connection:
|
||||
try:
|
||||
self.stop_port_monitoring()
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
self.append_file_exec_text("[INFO] Соединение закрыто.\n")
|
||||
except Exception as e:
|
||||
self.append_file_exec_text(f"[ERROR] Ошибка при закрытии соединения: {str(e)}\n")
|
||||
|
||||
self.current_command_index = 0 # Сбрасываем индекс текущей команды
|
||||
self.progress_bar['value'] = 0 # Сбрасываем прогресс-бар
|
||||
self.update_progress() # Обновляем отображение прогресса
|
||||
self.append_file_exec_text("[INFO] Выполнение остановлено.\n")
|
||||
self.reset_execution_buttons() # Сбрасываем состояние кнопок
|
||||
self.update_status_bar() # Обновляем статус бар
|
||||
|
||||
def reset_execution_buttons(self):
|
||||
self.start_button.config(state="normal")
|
||||
@@ -1162,6 +1346,28 @@ class SerialAppGUI(tk.Tk):
|
||||
self.stop_button.config(state="disabled")
|
||||
self.execution_paused = False
|
||||
self.execution_stop = False
|
||||
self.timer_running = False
|
||||
|
||||
def update_timer(self):
|
||||
"""Обновление таймера в главном потоке"""
|
||||
if self.timer_running:
|
||||
if not self.execution_paused:
|
||||
self.elapsed_time = time.time() - self.start_time
|
||||
hours = int(self.elapsed_time // 3600)
|
||||
minutes = int((self.elapsed_time % 3600) // 60)
|
||||
seconds = int(self.elapsed_time % 60)
|
||||
self.timer_label.config(text=f"Время: {hours:02d}:{minutes:02d}:{seconds:02d}")
|
||||
self.after(1000, self.update_timer) # Обновление каждую секунду
|
||||
|
||||
def update_progress(self):
|
||||
"""Обновление прогресса в главном потоке"""
|
||||
if hasattr(self, 'commands'):
|
||||
total_commands = len(self.commands)
|
||||
current = self.current_command_index
|
||||
self.progress_label.config(text=f"Прогресс: {current}/{total_commands} команд")
|
||||
if total_commands > 0:
|
||||
progress = (current / total_commands) * 100
|
||||
self.progress_bar['value'] = progress
|
||||
|
||||
# Создание вкладки "Редактор конфигурационного файла"
|
||||
def create_config_editor_tab(self, frame):
|
||||
@@ -1551,11 +1757,10 @@ class SerialAppGUI(tk.Tk):
|
||||
|
||||
# Обновляем метод update_status_bar
|
||||
def update_status_bar(self):
|
||||
# Обновляем индикатор подключения COM порта
|
||||
if self.connection:
|
||||
self.connection_indicator.configure(text="⬤", foreground='green')
|
||||
else:
|
||||
self.connection_indicator.configure(text="⬤", foreground='red')
|
||||
"""Обновление статус-бара"""
|
||||
# Проверяем реальное состояние подключения
|
||||
is_connected = bool(self.connection and self.connection.is_open)
|
||||
self.update_connection_indicator(is_connected)
|
||||
|
||||
# Обновляем индикатор TFTP сервера
|
||||
if self.tftp_server and self.tftp_server.running:
|
||||
|
||||
Reference in New Issue
Block a user