sync
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -20,3 +20,6 @@ Thumbs.db
|
|||||||
# Tooling / IDEs (optional)
|
# Tooling / IDEs (optional)
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# raw dateien
|
||||||
|
*.raw
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import argparse
|
|||||||
import sys
|
import sys
|
||||||
from core.config import load_config
|
from core.config import load_config
|
||||||
from core.connection import BuzzerConnection, BuzzerError
|
from core.connection import BuzzerConnection, BuzzerError
|
||||||
from core.commands import info, ls
|
from core.commands import info, ls, put
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Edis Buzzer Host Tool")
|
parser = argparse.ArgumentParser(description="Edis Buzzer Host Tool")
|
||||||
@@ -24,6 +24,11 @@ def main():
|
|||||||
ls_parser.add_argument("path", nargs="?", default="/", help="Zielpfad (Standard: /)")
|
ls_parser.add_argument("path", nargs="?", default="/", help="Zielpfad (Standard: /)")
|
||||||
ls_parser.add_argument("-r", "--recursive", action="store_true", help="Rekursiv auflisten")
|
ls_parser.add_argument("-r", "--recursive", action="store_true", help="Rekursiv auflisten")
|
||||||
|
|
||||||
|
# Befehl: put
|
||||||
|
put_parser = subparsers.add_parser("put", help="Lädt eine oder mehrere Dateien auf den Controller hoch")
|
||||||
|
put_parser.add_argument("sources", nargs="+", help="Lokale Quelldatei(en) oder Wildcards (z.B. *.raw)")
|
||||||
|
put_parser.add_argument("target", type=str, help="Zielpfad auf dem Controller (Verzeichnis muss mit '/' enden)")
|
||||||
|
|
||||||
# Argumente parsen
|
# Argumente parsen
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
config = load_config(args)
|
config = load_config(args)
|
||||||
@@ -54,6 +59,8 @@ def main():
|
|||||||
print(" (Leer)")
|
print(" (Leer)")
|
||||||
else:
|
else:
|
||||||
ls.print_tree(tree, path=args.path )
|
ls.print_tree(tree, path=args.path )
|
||||||
|
elif args.command == "put":
|
||||||
|
put.execute(conn, sources=args.sources, target=args.target)
|
||||||
elif args.command == "info" or args.command is None:
|
elif args.command == "info" or args.command is None:
|
||||||
# Wurde kein Befehl oder explizit 'info' angegeben, sind wir hier schon fertig
|
# Wurde kein Befehl oder explizit 'info' angegeben, sind wir hier schon fertig
|
||||||
pass
|
pass
|
||||||
|
|||||||
67
buzzer_tool/core/commands/put.py
Normal file
67
buzzer_tool/core/commands/put.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# core/commands/put.py
|
||||||
|
import os
|
||||||
|
import zlib
|
||||||
|
import glob
|
||||||
|
from core.connection import BuzzerError
|
||||||
|
|
||||||
|
def get_file_crc32(filepath: str) -> int:
|
||||||
|
"""Berechnet die IEEE CRC32-Prüfsumme einer Datei in Chunks."""
|
||||||
|
crc = 0
|
||||||
|
with open(filepath, 'rb') as f:
|
||||||
|
while chunk := f.read(4096):
|
||||||
|
crc = zlib.crc32(chunk, crc)
|
||||||
|
return crc & 0xFFFFFFFF
|
||||||
|
|
||||||
|
def execute(conn, sources: list, target: str):
|
||||||
|
"""
|
||||||
|
Lädt Dateien auf den Mikrocontroller hoch.
|
||||||
|
"""
|
||||||
|
# 1. Globbing auflösen (Notwendig für Windows, da die CMD Wildcards nicht auflöst)
|
||||||
|
resolved_files = []
|
||||||
|
for src in sources:
|
||||||
|
matches = glob.glob(src)
|
||||||
|
if matches:
|
||||||
|
resolved_files.extend(matches)
|
||||||
|
else:
|
||||||
|
print(f"Warnung: Datei '{src}' nicht gefunden.")
|
||||||
|
|
||||||
|
if not resolved_files:
|
||||||
|
print("Keine gültigen Dateien zum Übertragen gefunden.")
|
||||||
|
return
|
||||||
|
|
||||||
|
is_target_dir = target.endswith('/')
|
||||||
|
|
||||||
|
# Wenn mehrere Dateien angegeben sind, muss das Ziel zwingend ein Verzeichnis sein
|
||||||
|
if len(resolved_files) > 1 and not is_target_dir:
|
||||||
|
print("Fehler: Bei mehreren Quelldateien muss das Ziel ein Verzeichnis sein (mit '/' enden).")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. Transfer-Schleife
|
||||||
|
for filepath in resolved_files:
|
||||||
|
if not os.path.isfile(filepath):
|
||||||
|
print(f"Überspringe '{filepath}' (ist keine Datei)")
|
||||||
|
continue
|
||||||
|
|
||||||
|
filename = os.path.basename(filepath)
|
||||||
|
filesize = os.path.getsize(filepath)
|
||||||
|
crc32 = get_file_crc32(filepath)
|
||||||
|
|
||||||
|
dest_path = f"{target}{filename}" if is_target_dir else target
|
||||||
|
|
||||||
|
size_kb = filesize / 1024
|
||||||
|
print(f"Sende 📄 {filename} \033[90m({size_kb:.1f} KB)\033[0m nach {dest_path} ... ", end="", flush=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# PUT-Befehl ohne Warten auf 'OK' senden
|
||||||
|
cmd = f"put {dest_path};{filesize};{crc32}\n"
|
||||||
|
conn.serial.write(cmd.encode('utf-8'))
|
||||||
|
conn.serial.flush()
|
||||||
|
|
||||||
|
# Warten auf READY, Binärdaten senden und auf abschließendes OK warten
|
||||||
|
conn.send_binary(filepath)
|
||||||
|
|
||||||
|
print("\033[32mErfolgreich\033[0m")
|
||||||
|
except BuzzerError as e:
|
||||||
|
print(f"\n ❌ \033[31mFehler vom Controller: {e}\033[0m")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n ❌ \033[31mÜbertragungsfehler: {e}\033[0m")
|
||||||
@@ -63,49 +63,49 @@ class BuzzerConnection:
|
|||||||
|
|
||||||
raise TimeoutError(f"Lese-Timeout ({eff_timeout}s) beim Warten auf Antwort für: '{command}'")
|
raise TimeoutError(f"Lese-Timeout ({eff_timeout}s) beim Warten auf Antwort für: '{command}'")
|
||||||
|
|
||||||
def send_binary(self, filepath: str, chunk_size: int = 512, timeout: float = 10.0):
|
def send_binary(self, filepath: str, chunk_size: int = 512, timeout: float = 10.0):
|
||||||
"""
|
"""
|
||||||
Überträgt eine Binärdatei in Chunks, nachdem das READY-Signal empfangen wurde.
|
Überträgt eine Binärdatei in Chunks, nachdem das READY-Signal empfangen wurde.
|
||||||
"""
|
"""
|
||||||
# 1. Warte auf die READY-Bestätigung vom Controller
|
# 1. Warte auf die READY-Bestätigung vom Controller
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
ready = False
|
ready = False
|
||||||
while (time.time() - start_time) < timeout:
|
while (time.time() - start_time) < timeout:
|
||||||
if self.serial.in_waiting > 0:
|
if self.serial.in_waiting > 0:
|
||||||
line = self.serial.readline().decode('utf-8', errors='ignore').strip()
|
line = self.serial.readline().decode('utf-8', errors='ignore').strip()
|
||||||
if line == "READY":
|
if line == "READY":
|
||||||
ready = True
|
ready = True
|
||||||
break
|
break
|
||||||
elif line.startswith("ERR"):
|
elif line.startswith("ERR"):
|
||||||
raise BuzzerError(f"Fehler vor Binärtransfer: {line}")
|
raise BuzzerError(f"Fehler vor Binärtransfer: {line}")
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
if not ready:
|
if not ready:
|
||||||
raise TimeoutError("Kein READY-Signal vom Controller empfangen.")
|
raise TimeoutError("Kein READY-Signal vom Controller empfangen.")
|
||||||
|
|
||||||
# 2. Sende die Datei in Blöcken
|
# 2. Sende die Datei in Blöcken
|
||||||
file_size = os.path.getsize(filepath)
|
file_size = os.path.getsize(filepath)
|
||||||
bytes_sent = 0
|
bytes_sent = 0
|
||||||
|
|
||||||
with open(filepath, 'rb') as f:
|
with open(filepath, 'rb') as f:
|
||||||
while bytes_sent < file_size:
|
while bytes_sent < file_size:
|
||||||
chunk = f.read(chunk_size)
|
chunk = f.read(chunk_size)
|
||||||
if not chunk:
|
if not chunk:
|
||||||
break
|
break
|
||||||
self.serial.write(chunk)
|
self.serial.write(chunk)
|
||||||
# Flush blockiert, bis die Daten an den OS-USB-Treiber übergeben wurden
|
# Flush blockiert, bis die Daten an den OS-USB-Treiber übergeben wurden
|
||||||
self.serial.flush()
|
self.serial.flush()
|
||||||
bytes_sent += len(chunk)
|
bytes_sent += len(chunk)
|
||||||
|
|
||||||
# 3. Warte auf das finale OK (oder ERR bei CRC/Schreib-Fehlern)
|
# 3. Warte auf das finale OK (oder ERR bei CRC/Schreib-Fehlern)
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
while (time.time() - start_time) < timeout:
|
while (time.time() - start_time) < timeout:
|
||||||
if self.serial.in_waiting > 0:
|
if self.serial.in_waiting > 0:
|
||||||
line = self.serial.readline().decode('utf-8', errors='ignore').strip()
|
line = self.serial.readline().decode('utf-8', errors='ignore').strip()
|
||||||
if line == "OK":
|
if line == "OK":
|
||||||
return True
|
return True
|
||||||
elif line.startswith("ERR"):
|
elif line.startswith("ERR"):
|
||||||
raise BuzzerError(f"Fehler beim Speichern der Binärdatei: {line}")
|
raise BuzzerError(f"Fehler beim Speichern der Binärdatei: {line}")
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
raise TimeoutError("Zeitüberschreitung nach Binärtransfer (kein OK empfangen).")
|
raise TimeoutError("Zeitüberschreitung nach Binärtransfer (kein OK empfangen).")
|
||||||
Reference in New Issue
Block a user