Enhance error handling and retry mechanism for command execution
- Add a new `send_login_password()` function to centralize login/password input logic - Implement retry mechanism for both line and block command modes - Add support for detecting and handling command errors (marked by '^' symbol) - Limit retry attempts to 3 times for each command - Improve logging and error reporting for command execution - Update interactive command processing in SerialAppGUI to include retry logic
This commit is contained in:
245
ComConfigCopy.py
245
ComConfigCopy.py
@@ -43,6 +43,7 @@ from tkinter import ttk
|
|||||||
import serial
|
import serial
|
||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
from serial.serialutil import SerialException
|
from serial.serialutil import SerialException
|
||||||
|
from TFTPServer import TFTPServerThread
|
||||||
|
|
||||||
# Создаем необходимые папки
|
# Создаем необходимые папки
|
||||||
os.makedirs("Logs", exist_ok=True)
|
os.makedirs("Logs", exist_ok=True)
|
||||||
@@ -158,6 +159,31 @@ def create_connection(settings):
|
|||||||
logging.error(f"Неизвестная ошибка подключения: {e}", exc_info=True)
|
logging.error(f"Неизвестная ошибка подключения: {e}", exc_info=True)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Проверка наличия логина и пароля в настройках и отправка их на устройство
|
||||||
|
|
||||||
|
def send_login_password(serial_connection, login=None, password=None, is_gui=False):
|
||||||
|
"""Отправка логина и пароля на устройство."""
|
||||||
|
if not login:
|
||||||
|
if is_gui:
|
||||||
|
login = simpledialog.askstring("Login", "Введите логин:")
|
||||||
|
if login is None:
|
||||||
|
login = ""
|
||||||
|
else:
|
||||||
|
login = input("Введите логин: ")
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
if is_gui:
|
||||||
|
password = simpledialog.askstring("Password", "Введите пароль:", show="*")
|
||||||
|
if password is None:
|
||||||
|
password = ""
|
||||||
|
else:
|
||||||
|
password = getpass("Введите пароль: ")
|
||||||
|
|
||||||
|
|
||||||
|
# Чтение ответа от устройства с учётом таймаута.
|
||||||
|
|
||||||
def read_response(serial_connection, timeout, login=None, password=None, is_gui=False):
|
def read_response(serial_connection, timeout, login=None, password=None, is_gui=False):
|
||||||
"""
|
"""
|
||||||
Чтение ответа от устройства с учётом таймаута.
|
Чтение ответа от устройства с учётом таймаута.
|
||||||
@@ -184,28 +210,12 @@ def read_response(serial_connection, timeout, login=None, password=None, is_gui=
|
|||||||
last_line = lines[-1].strip()
|
last_line = lines[-1].strip()
|
||||||
|
|
||||||
if re.search(r'(login:|username:)$', last_line, re.IGNORECASE):
|
if re.search(r'(login:|username:)$', last_line, re.IGNORECASE):
|
||||||
if not login:
|
send_login_password(serial_connection, login, None, is_gui)
|
||||||
if is_gui:
|
|
||||||
login = simpledialog.askstring("Login", "Введите логин:")
|
|
||||||
if login is None:
|
|
||||||
login = ""
|
|
||||||
else:
|
|
||||||
login = input("Введите логин: ")
|
|
||||||
serial_connection.write((login + "\n").encode())
|
|
||||||
logging.info("Отправлен логин.")
|
|
||||||
response = b""
|
response = b""
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if re.search(r'(password:)$', last_line, re.IGNORECASE):
|
if re.search(r'(password:)$', last_line, re.IGNORECASE):
|
||||||
if not password:
|
send_login_password(serial_connection, None, password, is_gui)
|
||||||
if is_gui:
|
|
||||||
password = simpledialog.askstring("Password", "Введите пароль:", show="*")
|
|
||||||
if password is None:
|
|
||||||
password = ""
|
|
||||||
else:
|
|
||||||
password = getpass("Введите пароль: ")
|
|
||||||
serial_connection.write((password + "\n").encode())
|
|
||||||
logging.info("Отправлен пароль.")
|
|
||||||
response = b""
|
response = b""
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -256,6 +266,8 @@ def execute_commands_from_file(
|
|||||||
"""
|
"""
|
||||||
Выполнение команд из файла конфигурации.
|
Выполнение команд из файла конфигурации.
|
||||||
Если передан log_callback, вывод будет отображаться в GUI.
|
Если передан log_callback, вывод будет отображаться в GUI.
|
||||||
|
Теперь при обнаружении ошибки (например, если в ответе присутствует символ '^')
|
||||||
|
команда будет отправляться повторно.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with open(filename, "r", encoding="utf-8") as file:
|
with open(filename, "r", encoding="utf-8") as file:
|
||||||
@@ -264,26 +276,51 @@ def execute_commands_from_file(
|
|||||||
logging.info(msg)
|
logging.info(msg)
|
||||||
if log_callback:
|
if log_callback:
|
||||||
log_callback(msg)
|
log_callback(msg)
|
||||||
|
# Если выбран построчный режим
|
||||||
if copy_mode == "line":
|
if copy_mode == "line":
|
||||||
for cmd in lines:
|
for cmd in lines:
|
||||||
cmd = cmd.strip()
|
cmd = cmd.strip()
|
||||||
msg = f"\nОтправка команды: {cmd}\n"
|
max_attempts = 3
|
||||||
|
attempt = 0
|
||||||
|
while attempt < max_attempts:
|
||||||
|
msg = f"\nОтправка команды: {cmd} (Попытка {attempt+1} из {max_attempts})\n"
|
||||||
if log_callback:
|
if log_callback:
|
||||||
log_callback(msg)
|
log_callback(msg)
|
||||||
serial_connection.write((cmd + "\n").encode())
|
serial_connection.write((cmd + "\n").encode())
|
||||||
logging.info(f"Отправлена команда: {cmd}")
|
logging.info(f"Отправлена команда: {cmd} (Попытка {attempt+1})")
|
||||||
response = read_response(serial_connection, timeout, login=login, password=password, is_gui=is_gui)
|
response = read_response(serial_connection, timeout, login=login, password=password, is_gui=is_gui)
|
||||||
if response:
|
if response:
|
||||||
|
if '^' in response:
|
||||||
|
msg = (
|
||||||
|
f"[ERROR] Обнаружена ошибка при выполнении команды: {cmd}\n"
|
||||||
|
f"Ответ устройства:\n{response}\n"
|
||||||
|
f"Повторная отправка команды...\n"
|
||||||
|
)
|
||||||
|
if log_callback:
|
||||||
|
log_callback(msg)
|
||||||
|
logging.warning(f"Ошибка в команде: {cmd}. Повторная отправка.")
|
||||||
|
attempt += 1
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
msg = f"Ответ устройства:\n{response}\n"
|
msg = f"Ответ устройства:\n{response}\n"
|
||||||
if log_callback:
|
if log_callback:
|
||||||
log_callback(msg)
|
log_callback(msg)
|
||||||
logging.info(f"Ответ устройства:\n{response}")
|
logging.info(f"Ответ устройства:\n{response}")
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
msg = f"Ответ не получен для команды: {cmd}\n"
|
msg = f"Ответ не получен для команды: {cmd}\n"
|
||||||
if log_callback:
|
if log_callback:
|
||||||
log_callback(msg)
|
log_callback(msg)
|
||||||
logging.warning(f"Нет ответа для команды: {cmd}")
|
logging.warning(f"Нет ответа для команды: {cmd}")
|
||||||
|
break
|
||||||
|
if attempt == max_attempts:
|
||||||
|
msg = f"[ERROR] Команда не выполнена корректно после {max_attempts} попыток: {cmd}\n"
|
||||||
|
if log_callback:
|
||||||
|
log_callback(msg)
|
||||||
|
logging.error(msg)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
# Если выбран блочный режим
|
||||||
elif copy_mode == "block":
|
elif copy_mode == "block":
|
||||||
blocks = generate_command_blocks(lines, block_size)
|
blocks = generate_command_blocks(lines, block_size)
|
||||||
for block in blocks:
|
for block in blocks:
|
||||||
@@ -293,6 +330,61 @@ def execute_commands_from_file(
|
|||||||
serial_connection.write((block + "\n").encode())
|
serial_connection.write((block + "\n").encode())
|
||||||
logging.info(f"Отправлен блок команд:\n{block}")
|
logging.info(f"Отправлен блок команд:\n{block}")
|
||||||
response = read_response(serial_connection, timeout, login=login, password=password, is_gui=is_gui)
|
response = read_response(serial_connection, timeout, login=login, password=password, is_gui=is_gui)
|
||||||
|
# Если обнаружена ошибка в ответе на блок, отправляем команды по очереди
|
||||||
|
if response and '^' in response:
|
||||||
|
msg = (
|
||||||
|
f"[WARNING] Обнаружена ошибка при выполнении блока команд.\n"
|
||||||
|
f"Ответ устройства:\n{response}\n"
|
||||||
|
f"Пересылаются команды по отдельности...\n"
|
||||||
|
)
|
||||||
|
if log_callback:
|
||||||
|
log_callback(msg)
|
||||||
|
logging.warning("Ошибка в блочном режиме – отправляем команды индивидуально.")
|
||||||
|
for line in block.splitlines():
|
||||||
|
cmd = line.strip()
|
||||||
|
if not cmd:
|
||||||
|
continue
|
||||||
|
max_attempts = 3
|
||||||
|
attempt = 0
|
||||||
|
while attempt < max_attempts:
|
||||||
|
sub_msg = f"\nОтправка команды: {cmd} (Попытка {attempt+1} из {max_attempts})\n"
|
||||||
|
if log_callback:
|
||||||
|
log_callback(sub_msg)
|
||||||
|
serial_connection.write((cmd + "\n").encode())
|
||||||
|
logging.info(f"Отправлена команда: {cmd} (Попытка {attempt+1})")
|
||||||
|
sub_response = read_response(serial_connection, timeout, login=login, password=password, is_gui=is_gui)
|
||||||
|
if sub_response:
|
||||||
|
if '^' in sub_response:
|
||||||
|
sub_msg = (
|
||||||
|
f"[ERROR] Обнаружена ошибка при выполнении команды: {cmd}\n"
|
||||||
|
f"Ответ устройства:\n{sub_response}\n"
|
||||||
|
f"Повторная отправка команды...\n"
|
||||||
|
)
|
||||||
|
if log_callback:
|
||||||
|
log_callback(sub_msg)
|
||||||
|
logging.warning(f"Ошибка в команде: {cmd}. Повторная отправка.")
|
||||||
|
attempt += 1
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
sub_msg = f"Ответ устройства:\n{sub_response}\n"
|
||||||
|
if log_callback:
|
||||||
|
log_callback(sub_msg)
|
||||||
|
logging.info(f"Ответ устройства:\n{sub_response}")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
sub_msg = f"Ответ не получен для команды: {cmd}\n"
|
||||||
|
if log_callback:
|
||||||
|
log_callback(sub_msg)
|
||||||
|
logging.warning(f"Нет ответа для команды: {cmd}")
|
||||||
|
break
|
||||||
|
if attempt == max_attempts:
|
||||||
|
sub_msg = f"[ERROR] Команда не выполнена корректно после {max_attempts} попыток: {cmd}\n"
|
||||||
|
if log_callback:
|
||||||
|
log_callback(sub_msg)
|
||||||
|
logging.error(sub_msg)
|
||||||
|
time.sleep(1)
|
||||||
|
else:
|
||||||
if response:
|
if response:
|
||||||
msg = f"Ответ устройства:\n{response}\n"
|
msg = f"Ответ устройства:\n{response}\n"
|
||||||
if log_callback:
|
if log_callback:
|
||||||
@@ -315,98 +407,6 @@ def execute_commands_from_file(
|
|||||||
log_callback(msg)
|
log_callback(msg)
|
||||||
logging.error(f"Ошибка при выполнении команд из файла: {e}", exc_info=True)
|
logging.error(f"Ошибка при выполнении команд из файла: {e}", exc_info=True)
|
||||||
|
|
||||||
# ==========================
|
|
||||||
# Реализация TFTP-сервера для раздачи папки Firmware
|
|
||||||
# ==========================
|
|
||||||
|
|
||||||
class TFTPServerThread(threading.Thread):
|
|
||||||
def __init__(self, host, port, log_callback):
|
|
||||||
super().__init__()
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.log_callback = log_callback
|
|
||||||
self.running = True
|
|
||||||
self.sock = None
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
try:
|
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
self.sock.bind((self.host, self.port))
|
|
||||||
self.log_callback(f"TFTP сервер запущен на {self.host}:{self.port}\n")
|
|
||||||
except Exception as e:
|
|
||||||
self.log_callback(f"Ошибка запуска TFTP сервера: {e}\n")
|
|
||||||
return
|
|
||||||
|
|
||||||
while self.running:
|
|
||||||
try:
|
|
||||||
self.sock.settimeout(1)
|
|
||||||
data, addr = self.sock.recvfrom(1024)
|
|
||||||
if data:
|
|
||||||
self.handle_rrq(data, addr)
|
|
||||||
except socket.timeout:
|
|
||||||
continue
|
|
||||||
except Exception as e:
|
|
||||||
self.log_callback(f"Ошибка: {e}\n")
|
|
||||||
if self.sock:
|
|
||||||
self.sock.close()
|
|
||||||
self.log_callback("TFTP сервер остановлен.\n")
|
|
||||||
|
|
||||||
def handle_rrq(self, data, addr):
|
|
||||||
opcode = int.from_bytes(data[0:2], byteorder='big')
|
|
||||||
if opcode != 1:
|
|
||||||
self.log_callback(f"Получен не RRQ запрос от {addr}\n")
|
|
||||||
return
|
|
||||||
parts = data[2:].split(b'\0')
|
|
||||||
if len(parts) < 2:
|
|
||||||
self.log_callback(f"Неверный формат RRQ от {addr}\n")
|
|
||||||
return
|
|
||||||
req_filename = parts[0].decode('utf-8', errors='ignore')
|
|
||||||
mode = parts[1].decode('utf-8', errors='ignore')
|
|
||||||
self.log_callback(f"Получен RRQ для файла '{req_filename}' (режим {mode}) от {addr}\n")
|
|
||||||
filepath = os.path.join("Firmware", req_filename)
|
|
||||||
if not os.path.exists(filepath):
|
|
||||||
self.log_callback(f"Файл '{req_filename}' не найден в папке Firmware\n")
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
filesize = os.path.getsize(filepath)
|
|
||||||
f = open(filepath, 'rb')
|
|
||||||
except Exception as e:
|
|
||||||
self.log_callback(f"Ошибка открытия файла '{req_filename}': {e}\n")
|
|
||||||
return
|
|
||||||
block_num = 1
|
|
||||||
bytes_sent = 0
|
|
||||||
start_time = time.time()
|
|
||||||
while True:
|
|
||||||
block_data = f.read(512)
|
|
||||||
data_packet = b'\x00\x03' + block_num.to_bytes(2, byteorder='big') + block_data
|
|
||||||
self.sock.sendto(data_packet, addr)
|
|
||||||
self.log_callback(f"Отправлен блок {block_num} ({len(block_data)} байт)\n")
|
|
||||||
try:
|
|
||||||
self.sock.settimeout(5)
|
|
||||||
ack, ack_addr = self.sock.recvfrom(1024)
|
|
||||||
if ack_addr != addr:
|
|
||||||
continue
|
|
||||||
ack_opcode = int.from_bytes(ack[0:2], byteorder='big')
|
|
||||||
ack_block = int.from_bytes(ack[2:4], byteorder='big')
|
|
||||||
if ack_opcode != 4 or ack_block != block_num:
|
|
||||||
self.log_callback(f"Неверный ACK от {addr}\n")
|
|
||||||
break
|
|
||||||
except socket.timeout:
|
|
||||||
self.log_callback("Таймаут ожидания ACK\n")
|
|
||||||
break
|
|
||||||
bytes_sent += len(block_data)
|
|
||||||
elapsed = time.time() - start_time
|
|
||||||
speed = bytes_sent / elapsed if elapsed > 0 else 0
|
|
||||||
progress = (bytes_sent / filesize) * 100 if filesize > 0 else 0
|
|
||||||
self.log_callback(f"Прогресс: {progress:.2f}% | Скорость: {speed:.2f} байт/с\n")
|
|
||||||
if len(block_data) < 512:
|
|
||||||
break
|
|
||||||
block_num += 1
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.running = False
|
|
||||||
|
|
||||||
# ==========================
|
# ==========================
|
||||||
# Графический интерфейс (Tkinter) с улучшенной визуализацией
|
# Графический интерфейс (Tkinter) с улучшенной визуализацией
|
||||||
# ==========================
|
# ==========================
|
||||||
@@ -608,8 +608,11 @@ class SerialAppGUI(tk.Tk):
|
|||||||
|
|
||||||
def process_command(self, cmd):
|
def process_command(self, cmd):
|
||||||
try:
|
try:
|
||||||
|
max_attempts = 3
|
||||||
|
attempt = 0
|
||||||
|
while attempt < max_attempts:
|
||||||
self.connection.write((cmd + "\n").encode())
|
self.connection.write((cmd + "\n").encode())
|
||||||
logging.info(f"Отправлена команда: {cmd}")
|
logging.info(f"Отправлена команда: {cmd} (Попытка {attempt+1})")
|
||||||
response = read_response(
|
response = read_response(
|
||||||
self.connection,
|
self.connection,
|
||||||
self.settings.get("timeout", 10),
|
self.settings.get("timeout", 10),
|
||||||
@@ -618,11 +621,27 @@ class SerialAppGUI(tk.Tk):
|
|||||||
is_gui=True,
|
is_gui=True,
|
||||||
)
|
)
|
||||||
if response:
|
if response:
|
||||||
|
if '^' in response:
|
||||||
|
self.append_interactive_text(
|
||||||
|
f"[ERROR] Обнаружена ошибка при выполнении команды: {cmd}\n"
|
||||||
|
f"Ответ устройства:\n{response}\n"
|
||||||
|
f"Повторная отправка команды...\n"
|
||||||
|
)
|
||||||
|
logging.warning(f"Ошибка в команде: {cmd}. Попытка повторной отправки.")
|
||||||
|
attempt += 1
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
self.append_interactive_text(f"[INFO] Ответ устройства:\n{response}\n")
|
self.append_interactive_text(f"[INFO] Ответ устройства:\n{response}\n")
|
||||||
logging.info(f"Получен ответ:\n{response}")
|
logging.info(f"Получен ответ:\n{response}")
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
self.append_interactive_text("[WARN] Ответ не получен.\n")
|
self.append_interactive_text("[WARN] Ответ не получен.\n")
|
||||||
logging.warning("Нет ответа от устройства в течение таймаута.")
|
logging.warning("Нет ответа от устройства в течение таймаута.")
|
||||||
|
break
|
||||||
|
if attempt == max_attempts:
|
||||||
|
self.append_interactive_text(f"[ERROR] Команда не выполнена корректно после {max_attempts} попыток: {cmd}\n")
|
||||||
|
logging.error(f"Команда не выполнена корректно после {max_attempts} попыток: {cmd}")
|
||||||
except SerialException as e:
|
except SerialException as e:
|
||||||
self.append_interactive_text(f"[ERROR] Ошибка при отправке команды: {e}\n")
|
self.append_interactive_text(f"[ERROR] Ошибка при отправке команды: {e}\n")
|
||||||
logging.error(f"Ошибка отправки команды: {e}", exc_info=True)
|
logging.error(f"Ошибка отправки команды: {e}", exc_info=True)
|
||||||
|
|||||||
98
TFTPServer.py
Normal file
98
TFTPServer.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Модуль для TFTP-сервера.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
class TFTPServerThread(threading.Thread):
|
||||||
|
def __init__(self, host, port, log_callback):
|
||||||
|
super().__init__()
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.log_callback = log_callback
|
||||||
|
self.running = True
|
||||||
|
self.sock = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
self.sock.bind((self.host, self.port))
|
||||||
|
self.log_callback(f"TFTP сервер запущен на {self.host}:{self.port}\n")
|
||||||
|
except Exception as e:
|
||||||
|
self.log_callback(f"Ошибка запуска TFTP сервера: {e}\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
self.sock.settimeout(1)
|
||||||
|
data, addr = self.sock.recvfrom(1024)
|
||||||
|
if data:
|
||||||
|
self.handle_rrq(data, addr)
|
||||||
|
except socket.timeout:
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
self.log_callback(f"Ошибка: {e}\n")
|
||||||
|
if self.sock:
|
||||||
|
self.sock.close()
|
||||||
|
self.log_callback("TFTP сервер остановлен.\n")
|
||||||
|
|
||||||
|
def handle_rrq(self, data, addr):
|
||||||
|
opcode = int.from_bytes(data[0:2], byteorder='big')
|
||||||
|
if opcode != 1:
|
||||||
|
self.log_callback(f"Получен не RRQ запрос от {addr}\n")
|
||||||
|
return
|
||||||
|
parts = data[2:].split(b'\0')
|
||||||
|
if len(parts) < 2:
|
||||||
|
self.log_callback(f"Неверный формат RRQ от {addr}\n")
|
||||||
|
return
|
||||||
|
req_filename = parts[0].decode('utf-8', errors='ignore')
|
||||||
|
mode = parts[1].decode('utf-8', errors='ignore')
|
||||||
|
self.log_callback(f"Получен RRQ для файла '{req_filename}' (режим {mode}) от {addr}\n")
|
||||||
|
filepath = os.path.join("Firmware", req_filename)
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
self.log_callback(f"Файл '{req_filename}' не найден в папке Firmware\n")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
filesize = os.path.getsize(filepath)
|
||||||
|
f = open(filepath, 'rb')
|
||||||
|
except Exception as e:
|
||||||
|
self.log_callback(f"Ошибка открытия файла '{req_filename}': {e}\n")
|
||||||
|
return
|
||||||
|
block_num = 1
|
||||||
|
bytes_sent = 0
|
||||||
|
start_time = time.time()
|
||||||
|
while True:
|
||||||
|
block_data = f.read(512)
|
||||||
|
data_packet = b'\x00\x03' + block_num.to_bytes(2, byteorder='big') + block_data
|
||||||
|
self.sock.sendto(data_packet, addr)
|
||||||
|
self.log_callback(f"Отправлен блок {block_num} ({len(block_data)} байт)\n")
|
||||||
|
try:
|
||||||
|
self.sock.settimeout(5)
|
||||||
|
ack, ack_addr = self.sock.recvfrom(1024)
|
||||||
|
if ack_addr != addr:
|
||||||
|
continue
|
||||||
|
ack_opcode = int.from_bytes(ack[0:2], byteorder='big')
|
||||||
|
ack_block = int.from_bytes(ack[2:4], byteorder='big')
|
||||||
|
if ack_opcode != 4 or ack_block != block_num:
|
||||||
|
self.log_callback(f"Неверный ACK от {addr}\n")
|
||||||
|
break
|
||||||
|
except socket.timeout:
|
||||||
|
self.log_callback("Таймаут ожидания ACK\n")
|
||||||
|
break
|
||||||
|
bytes_sent += len(block_data)
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
speed = bytes_sent / elapsed if elapsed > 0 else 0
|
||||||
|
progress = (bytes_sent / filesize) * 100 if filesize > 0 else 0
|
||||||
|
self.log_callback(f"Прогресс: {progress:.2f}% | Скорость: {speed:.2f} байт/с\n")
|
||||||
|
if len(block_data) < 512:
|
||||||
|
break
|
||||||
|
block_num += 1
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.running = False
|
||||||
Reference in New Issue
Block a user