diff --git a/buzzer_tool/buzzer.py b/buzzer_tool/buzzer.py index afe0a95..a43de5b 100644 --- a/buzzer_tool/buzzer.py +++ b/buzzer_tool/buzzer.py @@ -3,7 +3,7 @@ import argparse import sys from core.config import load_config from core.connection import BuzzerConnection, BuzzerError -from core.commands import info, ls, put, mkdir, rm, confirm, reboot, play +from core.commands import info, ls, put, mkdir, rm, confirm, reboot, play, check def main(): parser = argparse.ArgumentParser(description="Edis Buzzer Host Tool") @@ -42,6 +42,10 @@ def main(): play_parser = subparsers.add_parser("play", help="Spielt eine Datei auf dem Controller ab") play_parser.add_argument("path", type=str, help="Pfad der abzuspielenden Datei (z.B. /lfs/a/neu)") + # Befehl: check + check_parser = subparsers.add_parser("check", help="Holt die CRC32 einer Datei und zeigt sie an") + check_parser.add_argument("path", type=str, help="Pfad der zu prüfenden Datei (z.B. /lfs/a/neu)") + # Befehl: confirm confirm_parser = subparsers.add_parser("confirm", help="Bestätigt die aktuell laufende Firmware") @@ -95,6 +99,12 @@ def main(): reboot.execute(conn) elif args.command == "play": play.execute(conn, path=args.path) + elif args.command == "check": + CRC32 = check.execute(conn, path=args.path) + if CRC32: + print(f"CRC32 von '{args.path}': 0x{CRC32['crc32']:08x}") + else: + print(f"Fehler: Keine CRC32-Information für '{args.path}' erhalten.") elif args.command == "info" or args.command is None: # Wurde kein Befehl oder explizit 'info' angegeben, sind wir hier schon fertig pass diff --git a/firmware/src/protocol.c b/firmware/src/protocol.c index 79c8c55..4829c84 100644 --- a/firmware/src/protocol.c +++ b/firmware/src/protocol.c @@ -351,6 +351,7 @@ int cmd_reboot_device() void cmd_play(const char *filename) { LOG_DBG("Play command received with filename: '%s'", filename); + audio_stop(); audio_play(filename); } @@ -366,6 +367,7 @@ int cmd_check(const char *param) return -ENOENT; } uint32_t crc32 = 0; + uint32_t start_time = k_uptime_get_32(); uint8_t buffer[256]; ssize_t read; while ((read = fs_read(&file, buffer, sizeof(buffer))) > 0) @@ -378,7 +380,8 @@ int cmd_check(const char *param) LOG_ERR("Check failed: error reading file '%s': %d", param, (int)read); return (int)read; } - LOG_DBG("Check successful: file '%s' has CRC32 0x%08x", param, crc32); + uint32_t duration = k_uptime_get_32() - start_time; + LOG_DBG("Check successful: file '%s' has CRC32 0x%08x, check took %u ms", param, crc32, duration); char response[64]; snprintf(response, sizeof(response), "CRC32 %s 0x%08x\n", param, crc32); usb_write_buffer((const uint8_t *)response, strlen(response)); diff --git a/webpage/package-lock.json b/webpage/package-lock.json index eedaf64..deeba9d 100644 --- a/webpage/package-lock.json +++ b/webpage/package-lock.json @@ -14,6 +14,7 @@ "@tailwindcss/vite": "^4.2.1", "astro": "^5.17.1", "astro-icon": "^1.1.5", + "phosphor-svelte": "^3.1.0", "svelte": "^5.53.5", "tailwindcss": "^4.2.1", "typescript": "^5.9.3" @@ -4981,6 +4982,25 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "license": "MIT" }, + "node_modules/phosphor-svelte": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/phosphor-svelte/-/phosphor-svelte-3.1.0.tgz", + "integrity": "sha512-nldtxx+XCgNREvrb7O5xgDsefytXpSkPTx8Rnu3f2qQCUZLDV1rLxYSd2Jcwckuo9lZB1qKMqGR17P4UDC0PrA==", + "license": "MIT", + "dependencies": { + "estree-walker": "^3.0.3", + "magic-string": "^0.30.13" + }, + "peerDependencies": { + "svelte": "^5.0.0 || ^5.0.0-next.96", + "vite": ">=5" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, "node_modules/piccolore": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", diff --git a/webpage/package.json b/webpage/package.json index 9ad520d..c77358b 100644 --- a/webpage/package.json +++ b/webpage/package.json @@ -15,6 +15,7 @@ "@tailwindcss/vite": "^4.2.1", "astro": "^5.17.1", "astro-icon": "^1.1.5", + "phosphor-svelte": "^3.1.0", "svelte": "^5.53.5", "tailwindcss": "^4.2.1", "typescript": "^5.9.3" diff --git a/webpage/src/components/FileRow.svelte b/webpage/src/components/FileRow.svelte index 30ef8c0..8d0c01d 100644 --- a/webpage/src/components/FileRow.svelte +++ b/webpage/src/components/FileRow.svelte @@ -1,42 +1,58 @@
-
+
{#if file.isSystem} - + {:else} - + {/if}
- -
- {file.name} - {file.size} + +
+ + {file.name} + + +
+
+ {#if !file.crc32 || isNaN(file.crc32)} + + {:else} + + {/if} + +
+ {file.crc32 ? `CRC32: 0x${file.crc32.toString(16).toUpperCase()}` : 'CRC unbekannt'} +
+
+
+ + + {file.size} + +
-
+
\ No newline at end of file diff --git a/webpage/src/components/FileStorage.svelte b/webpage/src/components/FileStorage.svelte index 8691869..d1240d0 100644 --- a/webpage/src/components/FileStorage.svelte +++ b/webpage/src/components/FileStorage.svelte @@ -2,10 +2,9 @@ import { buzzer } from '../lib/buzzerStore'; import { refreshFileList } from '../lib/buzzerActions'; import FileRow from './FileRow.svelte'; - + import { ArrowsCounterClockwiseIcon } from 'phosphor-svelte'; async function handleRefresh() { - // Falls wir den Port im Store haben, hier nutzen - // ansonsten wird er über die Actions verwaltet + console.log("Aktualisiere Dateiliste..."); await refreshFileList(); } @@ -19,9 +18,10 @@
@@ -44,14 +44,4 @@
{/if}
- -
- - - - - - - -
\ No newline at end of file diff --git a/webpage/src/lib/buzzerActions.ts b/webpage/src/lib/buzzerActions.ts index 6ef598d..46e88f6 100644 --- a/webpage/src/lib/buzzerActions.ts +++ b/webpage/src/lib/buzzerActions.ts @@ -1,4 +1,5 @@ // src/lib/buzzerActions.ts +import { nan } from 'astro:schema'; import { buzzer } from './buzzerStore'; let activePort: SerialPort | null = null; @@ -53,7 +54,10 @@ export async function updateDeviceInfo(port: SerialPort) { // WICHTIG: Auch hier "export" export async function refreshFileList(port: SerialPort) { - if (!port) return; + if (!port) { + if (!activePort) return; + port = activePort; + } let audioSize = 0; let sysSize = 0; const audioFiles: {name: string, size: string, crc32: number, isSystem: boolean}[] = []; @@ -73,7 +77,7 @@ export async function refreshFileList(port: SerialPort) { if (path.startsWith('/lfs/a')) { audioSize += size; console.log("Audio-Datei gefunden:", name); - audioFiles.push({ name, size: (size / 1024).toFixed(1) + " KB", crc32: 0, isSystem: false }); + audioFiles.push({ name, size: (size / 1024).toFixed(1) + " KB", crc32: NaN, isSystem: false, isSynced: false }); } else if (path.startsWith('/lfs/sys')) { sysSize += size; } diff --git a/webpage/src/lib/buzzerStore.ts b/webpage/src/lib/buzzerStore.ts index 9e54139..7921844 100644 --- a/webpage/src/lib/buzzerStore.ts +++ b/webpage/src/lib/buzzerStore.ts @@ -7,10 +7,10 @@ export const buzzer = writable({ build: 'unknown', storage: { total: 8.0, // 8 MB Flash laut Spezifikation - unknown: 8.0, available: 0.0, + unknown: 8.0, usedSys: 0.0, usedAudio: 0.0 }, - files: [] as {name: string, size: string, crc32: number, isSystem: boolean}[] + files: [] as {name: string, size: string, crc32: number, isSystem: boolean, isSynced: boolean}[] }); \ No newline at end of file