vchuserandClaude Opus 5 8082c4172b Zeige Erkennungsquelle je Eintrag in der Sitzungsliste
commitAssignment speichert je Eintrag bereits, ob per Barcode oder
Texterkennung erkannt wurde - das wurde bisher nirgends angezeigt. Die
Sitzungsliste zeigt es jetzt knapp neben jedem Eintrag, weil der
Unterschied beim Sortieren wertvoll ist: Barcode ist exakt, Texterkennung
fehleranfälliger.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:09:49 +02:00

RAM-Sortierhilfe

Browser-App, die per Handykamera gebrauchte Server-RAM-Module erkennt und beim physischen Sortieren am Tisch anleitet: Modul vor die Kamera halten, die App nennt den Stapel. Verglichen wird über alle Merkmale, die bei Modul und Stapel bekannt sind — Kapazität, Bauform, Rank, Geschwindigkeit und, sofern gelesen, Hersteller-Teilenummer (specsCompatible in src/spec.js); ein auf einer Seite fehlendes Merkmal wird dabei übersprungen, nicht als Widerspruch gewertet. Passt danach genau ein Stapel, wird das Modul zugewiesen; passen mehrere, fragt die App nach (proposeAssignment in src/session.js). Das hat eine bewusst in Kauf genommene Folge: Konnte die Teilenummer nicht gelesen werden — der häufige Fall bei überklebten oder beschädigten Etiketten — und passt anhand der übrigen Merkmale nur ein Stapel, landet das Modul dort, auch wenn seine tatsächliche Teilenummer eine andere wäre. Diese Abwägung ist Absicht: Ohne sie müsste die App bei jedem zweiten Modul nachfragen, sobald die Texterkennung keine Teilenummer liefert.

Die Erkennung läuft vollständig lokal im Browser — kein Server, keine Cloud, keine Daten verlassen das Gerät.

Funktionsweise

  1. Barcode zuerst. Code-128 und DataMatrix werden aus dem Kamerabild dekodiert (zxing-wasm). Das ist exakt, im Gegensatz zu Texterkennung. Werden mehrere Barcodes im Bild gefunden, die sich widersprechen (nicht dieselbe Teilenummer), gilt das als mehrdeutig — die App rät nicht, sondern fragt nach. Das gilt nur für Barcodes mit einem bekannten Nummernschema; widersprechen sich mehrere Barcodes mit unbekanntem Nummernschema, wird keine ihrer Teilenummern übernommen und das Ergebnis stützt sich allein auf die Texterkennung, ohne dass die App nachfragt.
  2. Teilenummer-Decoder. Aus einer Hersteller-PN wie M386A8K40BM1-CRC4Y werden Kapazität, Bauform und Geschwindigkeit tabellengesteuert abgeleitet. Gelingt das, entfällt OCR vollständig.
  3. OCR als Rückfallebene. Nur wenn kein Barcode eindeutig lesbar war (z. B. überklebtes oder beschädigtes Etikett). Das Kamerabild wird dafür in Graustufen gewandelt und mit einem Otsu-Schwellwert in Schwarz/Weiß aufbereitet: Der Schwellwert wird aus der Helligkeitsverteilung des gesamten Bildes bestimmt, nicht aus einer festen Kontrastspreizung zwischen hellstem und dunkelstem Pixel. Das macht die Aufbereitung robust gegen einzelne Lichtreflexe auf glänzenden Metalletiketten, die eine reine Min/Max-Spreizung leicht kippen würden. Das OCR-Ergebnis wird anschließend gegen die bekannten Spec-Werte abgeglichen, was die typischen Verwechslungen (0/O, 1/I, 8/B, …) auflöst.
  4. Stapel-Zuweisung. Grün (Barcode) und Gelb (OCR) laufen ohne Eingabe durch; nur bei roter Konfidenz (nichts Eindeutiges erkannt oder widersprüchliche Barcodes) oder mehrdeutiger Stapelzuordnung fragt die App nach.

Barcode- und OCR-Adapter sind gegen hängende Aufrufe abgesichert: Die Erkennungs-Pipeline (src/pipeline.js) bricht einen Barcode-Versuch nach 10 Sekunden und einen OCR-Versuch nach 20 Sekunden ab und behandelt das wie ein leeres Ergebnis, statt die App blockiert zu lassen. Diese Grenzen sind großzügig bemessen (sie müssen auch das einmalige Laden der WebAssembly-/Sprachmodule beim ersten Scan einer Sitzung abdecken) und keine Leistungserwartung.

Installation

npm install

Entwicklung

npm run dev

Der Server bindet an alle Interfaces; VCH stellt eine Preview-URL mit SSL bereit. HTTPS ist Pflicht — Kamerazugriff (getUserMedia) verlangt eine gesicherte Verbindung; über eine unverschlüsselte http://-Adresse verweigert der Browser die Kamera grundsätzlich, unabhängig davon, ob die App selbst korrekt funktioniert.

Tests

npm test

97 Tests, alle grün (Stand dieses Dokuments). Getestet werden ausschließlich die reinen Module ohne Browser-Zugriff: Teilenummer-Decoder, Spec-Normalisierung und toleranter Vergleich, OCR-Bildaufbereitung (Otsu-Schwellwert) und -Feldextraktion, Stapel-Zuweisung, die Erkennungs-Pipeline samt Zeitgrenzen und Mehrdeutigkeitsbehandlung, sowie die Sitzungssicherung samt Prüfung eines wiederhergestellten Zustands. Kamera, Barcode-/OCR-Adapter selbst und Oberfläche laufen nur im echten Browser und werden dort manuell geprüft (siehe „Am Gerät noch zu prüfen" unten) — node --test kennt kein DOM.

Produktion

npm run build
npm run preview

preview hört auf PORT aus der Umgebung (Vorgabe 4173, falls PORT nicht gesetzt ist).

Technik

  • Vanilla JavaScript (ES Modules), kein Framework
  • Vite als Entwicklungsserver und Bündler
  • zxing-wasm für Code-128 und DataMatrix
  • tesseract.js für Texterkennung
  • node:test für Tests, ohne zusätzliche Test-Bibliothek

Aufbau

Datei Verantwortung
src/spec.js Spec-Objekt, bekannte Werte, Fingerabdruck, toleranter Vergleich (Verwechslungstabelle für OCR-Fehler)
src/pn-tables.js Herstellertabellen für den Teilenummer-Decoder
src/pn-decoder.js Teilenummer → Spec-Felder
src/ocr-extract.js OCR-Rohtext → Spec-Felder
src/session.js Stapel halten, vorschlagen, buchen, umsortieren, zurücknehmen
src/pipeline.js Barcode → Teilenummer → OCR → Ampelfarbe, inkl. Zeitgrenzen und Mehrdeutigkeitsbehandlung
src/storage.js Absturzschutz der laufenden Sitzung: sichert und lädt aus localStorage, verwirft beim Laden jeden in sich unstimmigen Zustand vollständig (siehe unten)
src/camera.js Kamerastart, Einzelbildaufnahme, Bilddatei-Ersatzweg
src/barcode.js Adapter zu zxing-wasm
src/ocr.js Bildaufbereitung (Otsu) und Adapter zu tesseract.js
src/ui/ Ansichten: Scan-Ansicht, Ergebnis-Einblendung, Rot-Dialog bei Mehrdeutigkeit, Sitzungsliste
src/main.js Verdrahtung aller Module zur lauffähigen App
src/styles.css Farbvariablen (Grün/Gelb/Rot der Ampel-Rückmeldung), Layout des Kamera-Vollbilds samt Zielrahmen, Stapel-Leiste und Aktionsknöpfe sowie die Overlays für Treffer-Rückmeldung, Rot-Dialog und Sitzungsliste

spec, pn-decoder, ocr-extract, session, pipeline und storage sind reine Funktionen ohne Browser-Zugriff (kein window, document oder localStorage direkt) und deshalb vollständig mit node:test prüfbar. camera.js, barcode.js, ocr.js und src/ui/ brauchen einen echten Browser und werden nur manuell geprüft.

Sitzungssicherung im Detail

src/storage.js prüft eine aus localStorage geladene Sitzung gründlich, bevor sie übernommen wird: Stapel- und Eintragslisten müssen die erwartete Form haben, Stapelkennungen und Eintragsnummern müssen jeweils eindeutig sein, jeder Eintrag muss auf einen existierenden Stapel zeigen, und nextEntryId muss größer sein als jede vorhandene Eintragsnummer. Trifft auch nur eine dieser Bedingungen nicht zu, wird die gesamte gespeicherte Sitzung verworfen (die App startet leer), statt mit einem halb kaputten Zustand lautlos weiterzuarbeiten. Der gespeicherte Stapelzähler selbst wird nie übernommen, sondern beim Laden immer neu aus der tatsächlichen Zahl der zugeordneten Einträge berechnet.

Grenzen

  • Keine Bestandsführung über Sitzungen hinweg, kein Export. Die Sicherung in localStorage ist ausdrücklich nur ein Absturzschutz für die laufende Sitzung, keine dauerhafte Datenhaltung.
  • Das Laden der Seite braucht eine Verbindung; das Sortieren selbst nicht (nach dem Laden läuft alles lokal im Browser).
  • Der Teilenummer-Decoder kennt bisher nur das Samsung-DDR4-Schema. Unbekannte Schemata sind kein Fehler — sie führen automatisch zum OCR-Weg.
  • Der Teilenummer-Decoder ist nur teilweise gegen reale Module geprüft. Belegt ist ausschließlich der Samsung-Eintrag A8K40 (64 GB, 4DRx4) mit dem Geschwindigkeitscode CRC, anhand des Referenzmoduls M386A8K40BM1-CRC4Y. Alle übrigen Einträge in src/pn-tables.js — die Bauform-Codes (378, 391, 393, 386, 471, 474) und die übrigen Geschwindigkeitscodes (CPB, CTD, CVF, CWE) — stammen aus der veröffentlichten Samsung-Systematik und wurden nicht gegen ein reales Etikett bestätigt. Hynix- und Micron-Module haben noch gar keinen Tabelleneintrag und laufen vollständig über den OCR-Weg. Ein falscher Tabelleneintrag fällt auf dem Barcode-Weg nicht automatisch auf, weil dort keine Texterkennung gegenläuft, gegen die er widersprechen könnte — die Validierung braucht deshalb echte Module, keine Code-Prüfung.
  • Farb- und Schriftgestaltung ist bewusst schlicht gehalten und kann nach dem ersten Einsatz am Tisch nachgezogen werden.

Herstellertabellen erweitern

src/pn-tables.js ist tabellengesteuert. Ein neuer Hersteller oder ein neuer Dichte-/Geschwindigkeitscode braucht einen Eintrag mit pattern, formFactor, density und speed — keine Codeänderung an pn-decoder.js.

Vorgehen für jedes real vorliegende Modul:

  1. Hersteller-Teilenummer vom Etikett ablesen (Barcode oder Klartext) sowie die dazugehörige Klartextzeile mit Kapazität, Rank/Bauform und Geschwindigkeit.
  2. Einen Testfall in test/pn-decoder.test.js ergänzen, der die erwarteten Spec-Felder für genau diese Teilenummer festhält.
  3. Den passenden Tabelleneintrag (bzw. die passende Zeile in einer bestehenden Tabelle) in src/pn-tables.js ergänzen, bis der Testfall grün ist.
  4. Den Kommentar am Tabellenkopf aktualisieren, welche Einträge damit belegt sind.

Ohne reale Module bleiben unbestätigte Einträge unbestätigt — sie sollten nicht "auf Verdacht" ergänzt werden, weil ein falscher Eintrag auf dem Barcode-Weg (siehe „Grenzen") nicht mehr auffällt.

Am Gerät noch zu prüfen

Diese Umgebung hat keinen Browser und kein Handy. Die folgenden Punkte wurden während der Umsetzung deshalb nur durch Quelltext-Lektüre, node --check, npm test und npm run build abgesichert, aber nie am echten Gerät gesehen. Sie sollten vor dem produktiven Einsatz am Tisch geprüft werden:

Kamera und Aufnahme

  • Preview-URL auf dem Handy öffnen (muss https:// sein), Rückkamera-Bild erscheint im Vorschaubereich, "Kamera läuft" wird angezeigt.
  • Verweigerte Kamera-Berechtigung: verständliche Fehlermeldung erscheint und die Aufnahme-LED des Geräts erlischt tatsächlich wieder.
  • Aufnahme-Knopf antippen, bevor das Kamerabild bereit ist: verständliche Meldung statt technischer Fehler.
  • Datei-Auswahl als Ersatzweg (ohne Kamera) öffnet den nativen Dialog und hinterlässt keine leere Fläche im Layout.

Barcode- und OCR-Erkennung

  • Samsung-Referenzmodul scannen: Code-128 und DataMatrix werden erkannt, M386A8K40BM1-CRC4Y wird korrekt als Teilenummer übernommen, grüne Rückmeldung mit STAPEL A, 64GB 4DRx4 PC4-2400 LRDIMM.
  • Etikett ohne lesbaren Barcode fotografieren: OCR-Weg greift, die Otsu-Aufbereitung liefert auf einem echten, glänzenden Etikettenfoto tatsächlich brauchbaren Text (bisher nur an synthetischen Testbildern geprüft, nie an einem echten Foto).
  • Ladezeit des WASM-Barcode-Moduls (zxing-wasm) beim allerersten Scan einer Sitzung beobachten — bleibt sie deutlich unter der 10-Sekunden-Zeitgrenze der Pipeline, und braucht ein zweiter, schnell nachfolgender Scan nicht erneut die volle Ladezeit (Beleg, dass die Vorbereitung tatsächlich nur einmal läuft)?
  • Verbindung während des allerersten Scans einer Sitzung unterbrechen (WASM lädt per CDN), danach mit wiederhergestellter Verbindung erneut scannen: Das muss einen echten neuen Ladeversuch auslösen statt dauerhaft mit demselben Fehler zu scheitern.
  • Ladezeit des Tesseract-Arbeiters beim allerersten Scan einer Sitzung beobachten — bleibt sie auf einem normalen Handy deutlich unter der 20-Sekunden-Zeitgrenze der Pipeline?
  • Verfügbarkeitsanzeige der Texterkennung: bleibt nach einem erfolgreichen Ladevorgang dauerhaft "verfügbar", springt nach einem erzwungenen Fehlschlag (z. B. Flugmodus beim ersten Laden) beim nächsten Erfolg wieder darauf zurück?
  • Stichprobe, ob die feste Zeichen-Whitelist der Texterkennung auf echten Etiketten keine tatsächlich benötigten Zeichen ausschließt.
  • Bild ohne lesbaren Code: "kein Barcode gefunden" statt Fehler oder Absturz.

Bedienung und Anzeige

  • Zielrahmen liegt sinnvoll über dem Kamerabild, object-fit: cover füllt es passend.
  • Stapel-Leiste (A: 12, B: 4) aus rund einem Meter Entfernung mit einer Hand lesbar.
  • Grüne/gelbe Rückmeldung erscheint nach dem Scan und verschwindet nach rund 1200 ms von selbst; bei zwei Scans deutlich unter 1200 ms Abstand bleibt jeweils nur eine Rückmeldung sichtbar und die App bleibt bedienbar.
  • Alle Bedienflächen (Scan-, Rückgängig-, Stapel-Knöpfe) mit dem Daumen erreichbar, während die andere Hand das Modul hält.
  • Rückgängig-Knopf: Screenreader liest "Letzten Scan zurücknehmen" vor.
  • Rot-Dialog bei Mehrdeutigkeit: vier klar unterscheidbare Flächen ("Stapel A/B …", "neuer Stapel", "nochmal scannen"); bei vielen Stapel-Kandidaten bleibt alles per Scrollen erreichbar, ohne dass die Seite dahinter mitrollt; Tastatur-/Screenreader-Fokus bleibt innerhalb des Dialogs.

Sitzung

  • Ein Modul zweimal scannen: beide Male derselbe Stapel, Zähler steigt.
  • Rücknahme-Fläche nimmt genau den zuletzt erfassten Eintrag zurück, Zähler sinkt entsprechend.
  • Seite neu laden während einer laufenden Sitzung: Stapel und Zähler bleiben erhalten.
  • Sitzungsliste öffnen, Eintrag umsortieren, Eintrag entfernen: Ansicht baut sich korrekt neu auf.
  • "Sitzung beenden": Ansicht wird leer, und ein anschließendes Neuladen stellt keine alte Sitzung wieder her; ein danach neu gescanntes Modul wird aber wieder normal gegen Neuladen abgesichert.
  • Schnelles Mehrfachtippen auf die Stapel-Leiste öffnet nur eine Sitzungsliste, kein Übereinanderlegen mehrerer Listen.
S
Description
No description provided
Readme
687 KiB
Languages
JavaScript 95.4%
CSS 2.6%
Python 1.8%
HTML 0.2%