diff --git a/firmware/src/audio.c b/firmware/src/audio.c index b10ca42..a8a7c69 100644 --- a/firmware/src/audio.c +++ b/firmware/src/audio.c @@ -10,7 +10,10 @@ #include #include -LOG_MODULE_REGISTER(audio, LOG_LEVEL_INF); +#define AUDIO_THREAD_STACK_SIZE 2048 +#define AUDIO_THREAD_PRIORITY 5 + +LOG_MODULE_REGISTER(audio, LOG_LEVEL_DBG); /* Dauer eines Blocks in ms (4096 Bytes / (16kHz * 2 Kanäle * 2 Bytes)) = 64 ms */ #define BLOCK_DURATION_MS ((AUDIO_BLOCK_SIZE * 1000) / (AUDIO_SAMPLE_RATE * 2 * (AUDIO_WORD_WIDTH / 8))) diff --git a/firmware/src/audio.h b/firmware/src/audio.h index f927690..2256949 100644 --- a/firmware/src/audio.h +++ b/firmware/src/audio.h @@ -6,8 +6,6 @@ #define AUDIO_EVENT_STOP BIT(1) #define AUDIO_EVENT_SYNC BIT(8) -#define AUDIO_THREAD_STACK_SIZE 2048 -#define AUDIO_THREAD_PRIORITY 5 #define AUDIO_EVENTS_MASK (AUDIO_EVENT_PLAY | AUDIO_EVENT_STOP | AUDIO_EVENT_SYNC) #define AUDIO_BLOCK_SIZE 8192 /* 512 Samples Stereo (16-bit) = 8192 Bytes */ diff --git a/firmware/src/protocol.c b/firmware/src/protocol.c index 4829c84..b619e9b 100644 --- a/firmware/src/protocol.c +++ b/firmware/src/protocol.c @@ -18,7 +18,7 @@ LOG_MODULE_REGISTER(protocol, LOG_LEVEL_DBG); #define PROTOCOL_STACK_SIZE 2048 -#define PROTOCOL_PRIORITY 5 +#define PROTOCOL_PRIORITY 6 #define BUFFER_SIZE 256 static uint8_t buffer[BUFFER_SIZE]; diff --git a/webpage/src/lib/buzzerActions.ts b/webpage/src/lib/buzzerActions.ts index 46e88f6..4fa0958 100644 --- a/webpage/src/lib/buzzerActions.ts +++ b/webpage/src/lib/buzzerActions.ts @@ -1,37 +1,81 @@ -// src/lib/buzzerActions.ts -import { nan } from 'astro:schema'; import { buzzer } from './buzzerStore'; +import { get } from 'svelte/store'; -let activePort: SerialPort | null = null; -/** - * Hilfsfunktion für die Kommunikation - */ -async function sendCommand(port: SerialPort, command: string): Promise { - const encoder = new TextEncoder(); - const decoder = new TextDecoder(); - const writer = port.writable.getWriter(); - const reader = port.readable.getReader(); +type Task = { + command: string; + priority: number; // 0 = Hintergrund, 1 = User-Aktion + resolve: (lines: string[]) => void; + key?: string; +}; - try { - await writer.write(encoder.encode(command + "\n")); - writer.releaseLock(); +class SerialQueue { + private queue: Task[] = []; + private isProcessing = false; + private port: SerialPort | null = null; - let raw = ""; - while (true) { - const { value, done } = await reader.read(); - if (done) break; - raw += decoder.decode(value); - if (raw.includes("OK")) break; + setPort(port: SerialPort | null) { this.port = port; } + + async add(command: string, priority = 1, key?: string): Promise { + if (key) { + this.queue = this.queue.filter(t => t.key !== key); + } + + return new Promise((resolve) => { + const task = { command, priority, resolve, key }; + if (priority === 1) { + const lastUserTaskIndex = this.queue.findLastIndex(t => t.priority === 1); + this.queue.splice(lastUserTaskIndex + 1, 0, task); + } else { + this.queue.push(task); + } + this.process(); + }); + } + + private async process() { + if (this.isProcessing || !this.port || this.queue.length === 0) return; + this.isProcessing = true; + + const task = this.queue.shift()!; + try { + const encoder = new TextEncoder(); + const decoder = new TextDecoder(); + const writer = this.port.writable.getWriter(); + const reader = this.port.readable.getReader(); + + await writer.write(encoder.encode(task.command + "\n")); + writer.releaseLock(); + + let raw = ""; + while (true) { + const { value, done } = await reader.read(); + if (done) break; + raw += decoder.decode(value); + if (raw.includes("OK")) break; + } + reader.releaseLock(); + task.resolve(raw.split('\n').map(l => l.trim()).filter(l => l && l !== "OK" && !l.startsWith(task.command))); + } catch (e) { + console.error("Queue Error:", e); + } finally { + this.isProcessing = false; + this.process(); } - return raw.split('\n').map(l => l.trim()).filter(l => l && l !== "OK" && !l.startsWith(command)); - } finally { - reader.releaseLock(); } } -// WICHTIG: Hier muss "export" stehen! +const queue = new SerialQueue(); + +// --- EXPORTE --- + +export function setActivePort(port: SerialPort | null) { + queue.setPort(port); +} + +// Diese Funktion fehlte im letzten Block! export async function updateDeviceInfo(port: SerialPort) { - const lines = await sendCommand(port, "info"); + // Wir nutzen hier direkt die Queue für Konsistenz + const lines = await queue.add("info", 1); if (lines.length > 0) { const parts = lines[0].split(';'); if (parts.length >= 6) { @@ -52,76 +96,53 @@ export async function updateDeviceInfo(port: SerialPort) { } } -// WICHTIG: Auch hier "export" -export async function refreshFileList(port: SerialPort) { - if (!port) { - if (!activePort) return; - port = activePort; - } - let audioSize = 0; - let sysSize = 0; - const audioFiles: {name: string, size: string, crc32: number, isSystem: boolean}[] = []; +export async function refreshFileList() { + const lines = await queue.add("ls /lfs/a", 1, 'ls'); + const audioFiles = lines.map(line => { + const parts = line.split(','); + if (parts.length < 3) return null; + const [type, size, name] = parts; + return { + name, + size: (parseInt(size) / 1024).toFixed(1) + " KB", + crc32: 0, + isSystem: false + }; + }).filter(f => f !== null) as any[]; - async function scanDir(path: string) { - const lines = await sendCommand(port, `ls ${path}`); - for (const line of lines) { - const parts = line.split(','); - if (parts.length < 3) continue; - const [type, sizeStr, name] = parts; - const size = parseInt(sizeStr); - const fullPath = `${path}/${name}`; + buzzer.update(s => ({ ...s, files: audioFiles })); + startBackgroundCrcCheck(); +} - if (type === 'D') { - await scanDir(fullPath); - } else if (type === 'F') { - if (path.startsWith('/lfs/a')) { - audioSize += size; - console.log("Audio-Datei gefunden:", name); - audioFiles.push({ name, size: (size / 1024).toFixed(1) + " KB", crc32: NaN, isSystem: false, isSynced: false }); - } else if (path.startsWith('/lfs/sys')) { - sysSize += size; +async function startBackgroundCrcCheck() { + const currentFiles = get(buzzer).files; + for (const file of currentFiles) { + if (!file.crc32) { + const response = await queue.add(`check /lfs/a/${file.name}`, 0); + if (response.length > 0) { + const match = response[0].match(/0x([0-9a-fA-F]+)/); + if (match) { + const crc = parseInt(match[1], 16); + updateFileCrc(file.name, crc); } } } } - - await scanDir('/lfs'); - - buzzer.update(s => { - const bytesToMB = 1024 * 1024; - const usedMB = s.storage.total - s.storage.available; - const audioMB = audioSize / bytesToMB; - const sysMB = sysSize / bytesToMB; - const unknownMB = usedMB - audioMB - sysMB; - - return { - ...s, - files: audioFiles, - storage: { ...s.storage, usedAudio: audioMB, usedSys: sysMB, unknown: unknownMB > 0 ? unknownMB : 0 } - }; - }); } -export function setActivePort(port: SerialPort) { - activePort = port; +function updateFileCrc(name: string, crc: number) { + buzzer.update(s => ({ + ...s, + files: s.files.map(f => f.name === name ? { ...f, crc32: crc } : f) + })); } export async function playFile(filename: string) { - if (!activePort) { - console.warn("Kein aktiver Port zum Abspielen der Datei."); - return; - } - console.log(`Starte Wiedergabe: ${filename}`); - // Kommando: play /lfs/a/filename - await sendCommand(activePort, `play /lfs/a/${filename}`); + return queue.add(`play /lfs/a/${filename}`, 1, 'play'); } export async function deleteFile(filename: string) { - if (!activePort || !confirm(`Datei ${filename} wirklich löschen?`)) return; - - // Kommando: rm /lfs/a/filename - await sendCommand(activePort, `rm /lfs/a/${filename}`); - - // Liste nach dem Löschen aktualisieren - await refreshFileList(activePort); + if (!confirm(`Datei ${filename} wirklich löschen?`)) return; + await queue.add(`rm /lfs/a/${filename}`, 1); + await refreshFileList(); } \ No newline at end of file