Files
Aime/docs/07-betrieb.md
T
Claude 6973b46d75 fix(gui): Erklaertext und Partitionszeile aus der Speicherkarte
Endstand: Aufnahmen (GB + Dateizahl) mit Balken, Noch frei, Gesamt fuer
Aufnahmen. /api/sysstat liefert partPct und totalBytes weiter, die GUI
zeigt sie nur nicht mehr an.

Doku: Kapitel 07 + Changelog.
2026-08-17 22:25:55 +02:00

177 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 07 — Betrieb
> ⚠️ Dieses Repo ist **privat**; hier stehen LAN-Zugangsdaten. Nicht öffentlich stellen.
## Zugänge
| System | Zugang |
|---|---|
| Board SSH | `root@192.168.1.200` (WLAN, primär) / `root@192.168.1.50` (LAN), PW `123456` |
| Web-GUI | `http://192.168.1.200:3000` |
| MariaDB (auf dem Board) | DB `callrec`, User `callrec`, PW `callrec123` (kein `sqlite3`/CLI-Client — `mysql` nutzen) |
| Gitea (diese Doku) | `http://192.168.1.199:3000`, User `sascha` |
| App-User auf dem Board | `callrec`, Code in `/opt/callrec` |
## systemd-Einheiten
| Einheit | Scope | Aufgabe |
|---|---|---|
| `callrec.service` | system | Node-App (Web-GUI :3000, Aufnahme, Pipeline) |
| `callrec-btwatch.service` | system | BT-Headset-Auto-Connect (card-aware) |
| `callrec-perf.service` | system | CPU-Governor `performance` + A76-Pinning |
| `aime-usb-audio.service` | user | PipeWire-Topologie beim Boot (callmix, Loopbacks, Remap) |
| `aime-micgate.service` | user | LADSPA-Noise-Gate-Filter-Chain |
| `aime-volume-headset.service` | user | Windows-Volume → Headset-Sink (UAC2 numid=7) |
| `aime-headset-keys.service` | root | H390-Inline-Tasten → Lautstärke |
| `aime-iphone-route.service` | — | HFP-Routing-Watcher für iPhone-Anrufe. **Achtung:** die Unit liegt derzeit nicht unter `/etc/systemd/system/`, nur das Skript `/opt/callrec/aime-iphone-route.sh` ist da (Stand 2026-08-03, war schon vor dem eMMC-Umzug so) |
| `aime-bootsync.service` | system | spiegelt `/boot` der eMMC auf die SD-Karte (Start + Shutdown), siehe Speicher-Abschnitt |
| `filter-chain.service` (PipeWire-Standard) | user | **MASKIERT** — würde micgate doppeln |
| `aime-audio-out` / `aime-volume` | user | ES8323-Ära, **disabled** |
## Speicher: System auf der eMMC, Bootdateien auf der SD (seit 2026-08-03)
```mermaid
flowchart LR
ROM["BootROM RK3588S"] --> IDB["idbloader + U-Boot<br/>(eMMC, Sektor 64 / 16384)"]
IDB --> SCR{"boot.scr + armbianEnv.txt<br/>zuerst gefunden auf:"}
SCR -->|SD-Karte| KRN["Kernel + initrd<br/>von der SD"]
KRN -->|"rootdev=UUID=3ba18bc1…"| ROOT["/ = eMMC<br/>mmcblk0p1 „aime-emmc“"]
ROOT -.->|"aime-bootsync spiegelt /boot"| KRN
```
| Gerät | Größe | Rolle |
|---|---|---|
| `mmcblk0p1` (eMMC, `aime-emmc`) | 58 GB, 28 GB belegt | **`/` — System, Datenbank, Aufnahmen, Modelle** |
| `mmcblk1p1` (SD, `armbi_root`) | 118 GB | `boot.scr`/Kernel/initrd **und** eine vollständige Kopie des alten Systems als Rückfallebene. Nicht eingehängt — die SD wird im Betrieb weder gelesen noch beschrieben. |
**Warum die eMMC:** sie ist für Dauerbetrieb gebaut (Verschleiß auslesbar: `life_time 0x01`,
`pre_eol_info 0x01` = unter 10 % verbraucht), die SD-Karte ist der übliche Ausfallpunkt und stirbt
meist ohne Vorwarnung. Aime schreibt viel — jede Aufnahme WAV + MP3, dazu Datenbank und Suchindex.
**Der Haken, den man kennen muss:** U-Boot liest `boot.scr` und `armbianEnv.txt` von der **SD-Karte**,
solange sie steckt — nachgewiesen durch einen Neustart, bei dem eine auf die SD-Root zeigende
`armbianEnv.txt` das alte System hochfuhr. Der Kernel kommt also von der SD, das Root von der eMMC.
Ohne Gegenmaßnahme würde ein Kernel-Update auseinanderlaufen: neue Module auf der eMMC, alter Kernel
von der SD.
**`aime-bootsync.service`** löst das — spiegelt `/boot` beim Start **und** beim Herunterfahren auf die
SD (`rsync -a --delete --exclude='*.bak*'`), sodass beide Medien denselben Kernel tragen. Stirbt die
SD-Karte, findet U-Boot dort nichts mehr und nimmt die eMMC-Bootkette, die vollständig vorhanden ist.
Beide `armbianEnv.txt` (eMMC und SD) tragen `rootdev=UUID=3ba18bc1-bb8e-4a34-9c61-18a27f2c7adf`.
Auf der SD liegt daneben `armbianEnv.txt.bak-vor-emmc-umzug` mit der alten SD-UUID
`ab30e77b-…` — wer das alte System wieder booten will, kopiert die Datei zurück.
**Wichtig bei einem neuen Dateisystem:** `mkfs.ext4` aus e2fsprogs 1.47 setzt `orphan_file` und
`metadata_csum_seed`. `orphan_present` ist ein Incompat-Merkmal — ein Treiber, der es nicht kennt
(U-Boots ext4-Leser), verweigert das Lesen. Vor dem ersten Start entfernen:
```bash
tune2fs -O ^orphan_file,^metadata_csum_seed /dev/mmcblk0p1 && e2fsck -fy /dev/mmcblk0p1
```
## Wichtige Pfade auf dem Board
| Pfad | Inhalt |
|---|---|
| `/opt/callrec/*.js` | server / recorder / llm / diar / spk / db / bt / sysstat |
| `/opt/callrec/web/` | Vite-Frontend (`npx vite build` nach Änderungen an `src/`) |
| `/opt/callrec/aime-*.sh` | Setup-/Bridge-/Cap-Skripte |
| `/opt/callrec/milestones/m1-20260709/` | Meilenstein-1-Snapshot inkl. MANIFEST.md (Rollback) |
| `/opt/callrec/*.bak-*` | Datei-Backups je Änderung (z. B. `.bak-structsum`, `.bak-anchor`) |
| `/etc/sudoers.d/callrec-cpucap` | NOPASSWD für `aime-cpucap.sh` (Thermal-Cap) |
## Systemanzeige in der Kopfzeile (seit 2026-08-17)
Zwischen dem Tab „Bluetooth“ und dem Hell/Dunkel-Schalter stehen zwei Symbole, die den Zustand des
Boards ohne SSH zeigen:
| Symbol | Zeigt an | Klappkarte bei Mauskontakt |
|---|---|---|
| Ring + Prozentzahl | Anteil des **Aufnahme-Vorrats**, den die Aufnahmen schon belegen | Aufnahmen (GB + Dateizahl) mit Balken, Noch frei, Gesamt für Aufnahmen |
| Ventilator | CPU-Last — je höher, desto schneller die Drehung | CPU % (+ Kernzahl), RAM % mit GB, Temperatur mit Zone |
**Bezugsgröße der Prozentzahl** (Wunsch des Nutzers, 2026-08-17): nicht die Partition, sondern der Platz,
den ein *frisch aufgesetztes System dieser Ausbaustufe* für Aufnahmen übrig hätte:
```
100 % = freier Platz + Summe aller Audiodateien unter recordings/
belegt = Summe aller Audiodateien unter recordings/
```
System, Modelle und `node_modules` zählen also weder oben noch unten mit — die Anzeige beantwortet
„wie viel von meinem Aufnahme-Vorrat ist weg?“ und nicht „wie voll ist die eMMC?“. Die Klappkarte zeigt
bewusst nur die drei Zeilen Aufnahmen / Noch frei / Gesamt für Aufnahmen; den `df`-Wert (`partPct`) und
den Erklärtext zur 100-%-Bezugsgröße hat der Nutzer wieder streichen lassen. `/api/sysstat` liefert
`partPct` und `totalBytes` weiterhin.
**Mitgezählt wird jede Audiodatei unter `recordings/`, nicht nur die MP3s** — zu jeder Aufnahme liegt
dort auch die WAV-Rohdatei, und die ist rund viermal so groß wie das MP3 (Stand 2026-08-17: 52 MP3 =
0,39 GB, 51 WAV = 1,57 GB). Ebenso zählen die Aufnahmen im **Papierkorb** mit, denn deren Dateien
liegen bis zum Ablauf der 30 Tage weiter auf der Platte (Stand 2026-08-17: 16 aktiv, 36 im Papierkorb).
**Zeilenumbruch:** `.sys-row` steht auf `white-space: nowrap`, die Karte wächst stattdessen in die
Breite (`.sys-pop-disk { min-width: 285px }`). Vorher rutschte bei „Gesamt für Aufnahmen 27,5 GB“ die
Einheit allein in die nächste Zeile und sah aus wie ein fehlender Wert.
- Datenquelle: `GET /api/sysstat` (`app/sysstat.js`). `/proc`, `fs.statfsSync` und ein **gepufferter**
Größenscan über `recordings/` (`readdir` rekursiv + `stat`, TTL 60 s, nur Audio-Endungen) — **kein**
`df`/`du`/`top`-Prozess, damit die GUI im Sekundentakt fragen darf.
- CPU-% aus der Differenz zweier `/proc/stat`-Messpunkte. `sysstat.js` misst dafür **selbst** alle 2 s
(`setInterval(...).unref()`); ohne diesen eigenen Ticker wäre die erste Antwort nach jedem Start 0 %.
- RAM rechnet mit `MemAvailable`, nicht `MemFree` — sonst stünde das Board wegen Cache dauernd bei 95 %.
- Temperatur = wärmste der SoC-/Core-/Center-/GPU-/NPU-Zonen; `iwlwifi_1` (WLAN-Karte) fliegt raus, weil
ihr Wert nichts über die Rechenlast sagt.
- Frontend: `sysmonMarkup()` / `paintSysmon()` / `tickSysmon()` in `web/src/main.js`, Poll alle 5 s, im
Hintergrund-Tab (`document.hidden`) ausgesetzt. Ein einziger Ticker für alle `render()`-Durchläufe;
nach jedem `render()` werden die zuletzt geholten Werte sofort zurückgemalt, weil `render()` die
Kopfzeile komplett neu schreibt.
- Drehzahl des Ventilators läuft in **Stufen** (`spinSeconds()`: 7 s bei Leerlauf … 0,35 s ab 90 %). Eine
stufenlos gesetzte `animation-duration` würde die laufende Drehung bei jedem Poll neu ansetzen — das
ruckt sichtbar. `prefers-reduced-motion` stoppt die Animation ganz.
- Icon: vier gebogene Flügel (Pinwheel) im **feststehenden** Gehäusering — nur die Flügelgruppe dreht
sich, sonst wäre an einem runden Ring keine Drehung zu sehen.
- Farbe: grün < 75 %, gelb ab 75 %, rot ab 90 %; beim Ventilator zählt der schlimmste Wert aus CPU, RAM
und Temperatur (Temperatur skaliert 45 °C → 0 %, 90 °C → 100 %).
## Deploy-Ablauf (von Windows aus)
```mermaid
flowchart LR
DEV["Datei lokal ändern<br/>(pscp vom Board holen)"] --> BAK["Backup auf Board:<br/>cp X X.bak-&lt;thema&gt;"]
BAK --> UP["pscp hochladen"]
UP --> FE{"Frontend<br/>geändert?"}
FE -- ja --> BUILD["ssh: cd /opt/callrec/web && npx vite build"]
FE -- nein --> RESTART
BUILD --> RESTART["systemctl restart callrec"]
RESTART --> VERIFY["Verifizieren: journalctl -u callrec,<br/>GUI-Test, ggf. reanalyze-llm"]
VERIFY --> DOC["Doku + Changelog in Gitea aktualisieren"]
```
- Windows-Seite: PuTTY-Tools (`plink -batch -pw …`, `pscp`) — OpenSSH-Keys sind nicht hinterlegt.
- Node-Syntax vor Upload prüfen: `node --check datei.js`.
## Troubleshooting
| Symptom | Ursache / Fix |
|---|---|
| Zeile hängt auf `transcribing` | Absturz mitten in Diarization → GUI „Neu transkribieren“ (`/retranscribe`) |
| Summary wirkt leer nach Retranskription | GUI muss `analysis_status` pollen, nicht `status` — Analyse ist fire-and-forget |
| Board plötzlich aus | Thermik prüfen — Schutzabschaltung; Cap-Skript/Sudoers intakt? (Kap. 01) |
| Zu viele Sprecher | Audio geclippt? (Gain-Staging Kap. 02) Sonst „Analyse neu“ mit gepinnter Zahl |
| Teilnehmer ≠ Sprecher | Teilnehmer kommen aus Diarization; Namen im Sprecher-Editor vergeben |
| Kein far-end bei iPhone-Aufnahme | BT-Gerät `Trusted=yes`? HFP-Nodes erscheinen nur IM Anruf (Kap. 06) |
| GUI zeigt Quelle ohne Signal | `.monitor`-Falle — Quelle mit `parecord` + `ffmpeg astats` prüfen (Kap. 02) |
| `pactl load-module module-filter-chain` → „No such entity“ | Filter-Chain ist Core-Modul → Drop-in-Config nutzen (Kap. 02) |
| rkllm/Whisper `rc=-6` | RAM-Contention — nicht beide NPU-resident; NPU-Lock beachten (Kap. 05) |
| Analyse „KI Fehler“ bei langen Aufnahmen | Kontext-Überlauf → Chunk-Pfad greift; bei schwachem Ergebnis „Analyse neu“ |
## Offene Punkte (Stand 2026-07-16)
- Bedingter Takt-Cap (nur > 15 min Aufnahmedauer) — angeboten, nicht gebaut (~1,4× schnellere Kurzclips).
- Feinabstimmung Mikro/PC-Balance in einem echten Headset-Call.
- Glossar Phase 2: nächtlicher systemd-Timer + large-v3-Kandidaten-Vorschläge mit Bestätigung (verschoben).
- iPhone-Pfad: automatischer Aufnahme-Start + voller E2E-Test (Kap. 06).
- Qwen-1.5B als schnellerer LLM-Kandidat für Kurzclips (verschoben).
- Aufnahme #41 bleibt dauerhaft geclippt (Referenz für den Gain-Bug).