Aktualisiere README fuer Barcode-Codeabgleich und gelockerten Gruen-Massstab
Beschreibt die neue Vorrangregel (bekannter Barcode-Inhalt entscheidet vor dem Vergleich der technischen Angaben), dass eine exakt gelesene Teilenummer allein fuer Gruen genuegt, und den OCR-Kurzschluss bei bereits bekanntem Code. Ergaenzt entsprechende Pruefpunkte im Abschnitt "Am Gerät noch zu pruefen" und aktualisiert die Testanzahl auf 121.
This commit is contained in:
@@ -2,23 +2,37 @@
|
||||
|
||||
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. Ein Stapel gilt dabei nur als Kandidat, wenn er mit dem Modul
|
||||
mindestens ein tatsächlich erkanntes Merkmal teilt (`sharesSetField` in
|
||||
`src/session.js`) — ein Stapel ganz ohne erkannte Angaben (etwa aus einem
|
||||
manuell entschiedenen Rot-Fall) ist deshalb nie automatisch treffend, sonst
|
||||
würde er lautlos jedes folgende Modul an sich ziehen. 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.
|
||||
App nennt den Stapel.
|
||||
|
||||
Zwei Module gelten als zusammengehörig, wenn sie denselben Barcode-Inhalt
|
||||
tragen — ein Barcode ist exakt gelesen, das ist der zuverlässigste Maßstab.
|
||||
Jeder Stapel führt dafür die Menge der Barcode-Zeichenketten, die bei seinen
|
||||
Modulen gelesen wurden (`stack.codes`); trägt ein neu gescanntes Modul einen
|
||||
Code, den ein Stapel bereits kennt, gehört es dorthin — das hat Vorrang vor
|
||||
allem Weiteren, auch wenn die technischen Angaben unvollständig sind
|
||||
(`proposeAssignment` in `src/session.js`). Auf Etiketten stehen oft zwei
|
||||
Codes, von denen einer die Seriennummer ist, die bei jedem Modul anders ist:
|
||||
Sie läuft beim Codeabgleich einfach ins Leere, während die danebenstehende
|
||||
Teilenummer trägt — kein Sonderfall, nur eine Konsequenz des Mengenvergleichs.
|
||||
|
||||
Kennt kein Stapel einen der gelesenen Codes (typischerweise beim ersten Modul
|
||||
eines neuen Stapels), vergleicht die App stattdessen ü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. Ein Stapel gilt dabei nur als Kandidat, wenn
|
||||
er mit dem Modul mindestens ein tatsächlich erkanntes Merkmal teilt
|
||||
(`sharesSetField` in `src/session.js`) — ein Stapel ganz ohne erkannte Angaben
|
||||
(etwa aus einem manuell entschiedenen Rot-Fall) ist deshalb nie automatisch
|
||||
treffend, sonst würde er lautlos jedes folgende Modul an sich ziehen. Passt
|
||||
danach genau ein Stapel, wird das Modul zugewiesen; passen mehrere oder mehrere
|
||||
über einen bekannten Code, fragt die App nach. 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.
|
||||
@@ -31,25 +45,36 @@ keine Daten verlassen das Gerät.
|
||||
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.
|
||||
Nummernschema, wird keine ihrer Teilenummern übernommen.
|
||||
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.
|
||||
abgeleitet. Gelingt das, entfällt OCR vollständig, das Ergebnis ist grün.
|
||||
Kennt der Decoder das Nummernschema nicht (siehe „Grenzen“ unten), ist das
|
||||
allein noch kein Grund für Rot: Zum Sortieren muss keine Kapazität bekannt
|
||||
sein, eine exakt gelesene Teilenummer genügt, um ein Modul wiederzuerkennen
|
||||
(`recognize` in `src/pipeline.js`). Findet die anschließende Texterkennung
|
||||
dazu auch keine Kapazität, ist das Ergebnis trotzdem grün, nur eben ohne
|
||||
abgeleitete Angaben in der Rückmeldung. Ist einer der gelesenen Codes
|
||||
bereits einem Stapel dieser Sitzung bekannt, steht die Zuordnung ohnehin
|
||||
fest — dann entfällt auch die Texterkennung, weil sie nichts mehr beitragen
|
||||
könnte, das über die Zuordnung entscheidet. Bei einem tatsächlich neuen
|
||||
Modul läuft sie dagegen wie gehabt, denn dort liefert sie die lesbare
|
||||
Beschriftung des neuen Stapels.
|
||||
3. **OCR als Rückfallebene.** Nur wenn kein Barcode eindeutig lesbar war oder
|
||||
sein Nummernschema unbekannt ist (z. B. überklebtes/beschädigtes Etikett
|
||||
oder ein noch nicht in `src/pn-tables.js` hinterlegter Hersteller). 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.
|
||||
durch; nur bei roter Konfidenz (nichts Eindeutiges erkannt, widersprüchliche
|
||||
Barcodes oder widersprüchliche unbekannte Barcodes ohne jede OCR-Kapazität)
|
||||
oder mehrdeutiger Stapelzuordnung fragt die App nach.
|
||||
5. **Sitzungsfortsetzung beim Start.** Findet die App beim Laden eine in
|
||||
`localStorage` gesicherte, nicht leere Sitzung vor, wird sie nicht
|
||||
stillschweigend übernommen, sondern zur Fortsetzung oder zum Verwerfen
|
||||
@@ -96,7 +121,7 @@ korrekt funktioniert.
|
||||
npm test
|
||||
```
|
||||
|
||||
101 Tests, alle grün (Stand dieses Dokuments). Getestet werden ausschließlich
|
||||
121 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
|
||||
@@ -131,8 +156,8 @@ nicht gesetzt ist).
|
||||
| `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/session.js` | Stapel halten, vorschlagen, buchen, umsortieren, zurücknehmen — inklusive der je Stapel gesammelten Barcode-Inhalte (`stack.codes`), die vor dem Vergleich der technischen Angaben über die Zuordnung entscheiden |
|
||||
| `src/pipeline.js` | Barcode → Teilenummer → OCR → Ampelfarbe, inkl. Zeitgrenzen, Mehrdeutigkeitsbehandlung und Kurzschluss über einen bereits bekannten Barcode-Inhalt (`deps.isKnownCode`) |
|
||||
| `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` |
|
||||
@@ -163,7 +188,11 @@ 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.
|
||||
zugeordneten Einträge berechnet. Die je Stapel gesicherten Barcode-Inhalte
|
||||
(`stack.codes`) werden, sofern vorhanden, ebenso streng geprüft (Liste von
|
||||
Zeichenketten); ein gesicherter Stand aus einer Fassung vor dieser Erweiterung
|
||||
ohne dieses Feld gilt dagegen nicht als unstimmig, sondern wird beim Laden als
|
||||
Stapel ohne bekannte Codes wiederhergestellt.
|
||||
|
||||
## Grenzen
|
||||
|
||||
@@ -173,7 +202,11 @@ zugeordneten Einträge berechnet.
|
||||
- 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.
|
||||
Schemata sind kein Fehler — sie führen automatisch zum OCR-Weg, und selbst
|
||||
wenn dieser keine Kapazität findet, genügt die exakt gelesene Teilenummer
|
||||
allein für Grün (siehe „Funktionsweise“ oben). Nur Kapazität, Bauform,
|
||||
Rank und Geschwindigkeit hängen am Tabelleneintrag — die Wiedererkennung
|
||||
eines bereits erfassten Moduls über seinen Barcode-Inhalt nicht.
|
||||
- **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
|
||||
@@ -259,6 +292,16 @@ werden:
|
||||
Etiketten keine tatsächlich benötigten Zeichen ausschließt.
|
||||
- Bild ohne lesbaren Barcode und ohne verwertbaren OCR-Text: Rot-Dialog
|
||||
erscheint statt Fehler oder Absturz.
|
||||
- Modul mit unbekanntem Nummernschema (z. B. ein Hynix-Modul) scannen: grüne
|
||||
Rückmeldung statt Rot, auch wenn keine Kapazität abgeleitet werden kann.
|
||||
Dasselbe Modul ein zweites Mal scannen: landet auf demselben Stapel — und
|
||||
die Rückmeldung erscheint spürbar schneller als beim ersten Scan, weil die
|
||||
Texterkennung diesmal übersprungen wird (nur am Gerät beobachtbar, da
|
||||
`node --test` keine echte Ladezeit misst).
|
||||
- Etikett mit zwei Barcodes (Teile- *und* Seriennummer) mehrfach scannen:
|
||||
beide Scans landen auf demselben Stapel, obwohl die Seriennummer bei realen
|
||||
Modulen nie identisch ist — das belegt, dass die Teilenummer und nicht die
|
||||
Seriennummer die Zuordnung trägt.
|
||||
|
||||
**Bedienung und Anzeige**
|
||||
- Zielrahmen liegt sinnvoll über dem Kamerabild, `object-fit: cover` füllt
|
||||
|
||||
Reference in New Issue
Block a user