From 2d3a9b0ddbc44577503e5b6377080f012b18967a Mon Sep 17 00:00:00 2001 From: vchuser Date: Tue, 28 Jul 2026 17:42:29 +0200 Subject: [PATCH] docs: README und Projektbeschreibung README und .vch-description an den tatsaechlichen Code angeglichen: Otsu- Schwellwert statt fester Kontrastspreizung fuer OCR, Zeitgrenzen und Mehrdeutigkeitsbehandlung in der Pipeline, gruendliche Zustandspruefung in storage.js, 97 Tests. Herstellertabellen-Validierung (Step 3) bleibt blockiert, da sie reale Module vom Nutzer braucht - pn-tables.js unveraendert. Ehrlicher Beleg-Stand der Tabellen sowie Erweiterungsanleitung in der README, plus konsolidierte Liste dessen, was mangels Handy/Browser noch am Geraet zu pruefen ist. Co-Authored-By: Claude Opus 5 (1M context) --- .vch-description | 9 +- README.md | 240 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 231 insertions(+), 18 deletions(-) diff --git a/.vch-description b/.vch-description index e7c7d39..10fbbbe 100644 --- a/.vch-description +++ b/.vch-description @@ -1 +1,8 @@ -Describe your project here. +Sortierhilfe fuer gebrauchte Server-RAM-Module. Man haelt ein Modul vor die +Handykamera, und die App sagt sofort, auf welchen Stapel es gehoert - Module +mit identischen technischen Daten und identischer Hersteller-Teilenummer +landen zusammen, damit daraus verkaufsfertige Kits entstehen. + +Gedacht fuer den Wareneingang im Gebrauchthandel mit Serverkomponenten, wo +gemischte Chargen ankommen und von Hand sortiert werden muessen. Die App +ersetzt das Abtippen von Etiketten und das Vergleichen im Kopf. diff --git a/README.md b/README.md index 85d3dfd..3f5235a 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,243 @@ -# ocr_scanner +# RAM-Sortierhilfe -> Short description of what this project does. +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. Module mit identischer Spec **und** identischer +Hersteller-Teilenummer bilden einen Stapel, damit daraus verkaufsfertige Kits +entstehen. -## Getting Started +Die Erkennung läuft vollständig lokal im Browser — kein Server, keine Cloud, +keine Daten verlassen das Gerät. -### Prerequisites +## Funktionsweise -- Node.js 20+ -- npm or pnpm +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. +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. -### Installation +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 ```bash npm install ``` -### Development +## Entwicklung ```bash npm run dev ``` -## Tech Stack +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 -## Project Structure - -``` -src/ - ... +```bash +npm test ``` -## License +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 + +```bash +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](https://vite.dev/) als Entwicklungsserver und Bündler +- [`zxing-wasm`](https://github.com/Sec-ant/zxing-wasm) für Code-128 und DataMatrix +- [`tesseract.js`](https://tesseract.projectnaptha.com/) 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 | + +`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 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.