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) <noreply@anthropic.com>
This commit is contained in:
+8
-1
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user