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
|
Browser-App, die per Handykamera gebrauchte Server-RAM-Module erkennt und
|
||||||
beim physischen Sortieren am Tisch anleitet: Modul vor die Kamera halten, die
|
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
|
App nennt den Stapel.
|
||||||
Stapel bekannt sind — Kapazität, Bauform, Rank, Geschwindigkeit und, sofern
|
|
||||||
gelesen, Hersteller-Teilenummer (`specsCompatible` in `src/spec.js`); ein auf
|
Zwei Module gelten als zusammengehörig, wenn sie denselben Barcode-Inhalt
|
||||||
einer Seite fehlendes Merkmal wird dabei übersprungen, nicht als Widerspruch
|
tragen — ein Barcode ist exakt gelesen, das ist der zuverlässigste Maßstab.
|
||||||
gewertet. Ein Stapel gilt dabei nur als Kandidat, wenn er mit dem Modul
|
Jeder Stapel führt dafür die Menge der Barcode-Zeichenketten, die bei seinen
|
||||||
mindestens ein tatsächlich erkanntes Merkmal teilt (`sharesSetField` in
|
Modulen gelesen wurden (`stack.codes`); trägt ein neu gescanntes Modul einen
|
||||||
`src/session.js`) — ein Stapel ganz ohne erkannte Angaben (etwa aus einem
|
Code, den ein Stapel bereits kennt, gehört es dorthin — das hat Vorrang vor
|
||||||
manuell entschiedenen Rot-Fall) ist deshalb nie automatisch treffend, sonst
|
allem Weiteren, auch wenn die technischen Angaben unvollständig sind
|
||||||
würde er lautlos jedes folgende Modul an sich ziehen. Passt danach genau ein
|
(`proposeAssignment` in `src/session.js`). Auf Etiketten stehen oft zwei
|
||||||
Stapel, wird das Modul zugewiesen; passen mehrere, fragt die App nach
|
Codes, von denen einer die Seriennummer ist, die bei jedem Modul anders ist:
|
||||||
(`proposeAssignment` in `src/session.js`). Das hat eine bewusst in Kauf
|
Sie läuft beim Codeabgleich einfach ins Leere, während die danebenstehende
|
||||||
genommene Folge: Konnte die Teilenummer nicht gelesen
|
Teilenummer trägt — kein Sonderfall, nur eine Konsequenz des Mengenvergleichs.
|
||||||
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
|
Kennt kein Stapel einen der gelesenen Codes (typischerweise beim ersten Modul
|
||||||
wenn seine tatsächliche Teilenummer eine andere wäre. Diese Abwägung ist
|
eines neuen Stapels), vergleicht die App stattdessen über alle Merkmale, die
|
||||||
Absicht: Ohne sie müsste die App bei jedem zweiten Modul nachfragen, sobald
|
bei Modul und Stapel bekannt sind — Kapazität, Bauform, Rank, Geschwindigkeit
|
||||||
die Texterkennung keine Teilenummer liefert.
|
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,
|
Die Erkennung läuft vollständig lokal im Browser — kein Server, keine Cloud,
|
||||||
keine Daten verlassen das Gerät.
|
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,
|
dieselbe Teilenummer), gilt das als mehrdeutig — die App rät nicht,
|
||||||
sondern fragt nach. Das gilt nur für Barcodes mit einem bekannten
|
sondern fragt nach. Das gilt nur für Barcodes mit einem bekannten
|
||||||
Nummernschema; widersprechen sich mehrere Barcodes mit unbekanntem
|
Nummernschema; widersprechen sich mehrere Barcodes mit unbekanntem
|
||||||
Nummernschema, wird keine ihrer Teilenummern übernommen und das Ergebnis
|
Nummernschema, wird keine ihrer Teilenummern übernommen.
|
||||||
stützt sich allein auf die Texterkennung, ohne dass die App nachfragt.
|
|
||||||
2. **Teilenummer-Decoder.** Aus einer Hersteller-PN wie `M386A8K40BM1-CRC4Y`
|
2. **Teilenummer-Decoder.** Aus einer Hersteller-PN wie `M386A8K40BM1-CRC4Y`
|
||||||
werden Kapazität, Bauform und Geschwindigkeit tabellengesteuert
|
werden Kapazität, Bauform und Geschwindigkeit tabellengesteuert
|
||||||
abgeleitet. Gelingt das, entfällt OCR vollständig.
|
abgeleitet. Gelingt das, entfällt OCR vollständig, das Ergebnis ist grün.
|
||||||
3. **OCR als Rückfallebene.** Nur wenn kein Barcode eindeutig lesbar war (z. B.
|
Kennt der Decoder das Nummernschema nicht (siehe „Grenzen“ unten), ist das
|
||||||
überklebtes oder beschädigtes Etikett). Das Kamerabild wird dafür in
|
allein noch kein Grund für Rot: Zum Sortieren muss keine Kapazität bekannt
|
||||||
Graustufen gewandelt und mit einem **Otsu-Schwellwert** in Schwarz/Weiß
|
sein, eine exakt gelesene Teilenummer genügt, um ein Modul wiederzuerkennen
|
||||||
aufbereitet: Der Schwellwert wird aus der Helligkeitsverteilung des
|
(`recognize` in `src/pipeline.js`). Findet die anschließende Texterkennung
|
||||||
gesamten Bildes bestimmt, nicht aus einer festen Kontrastspreizung
|
dazu auch keine Kapazität, ist das Ergebnis trotzdem grün, nur eben ohne
|
||||||
zwischen hellstem und dunkelstem Pixel. Das macht die Aufbereitung robust
|
abgeleitete Angaben in der Rückmeldung. Ist einer der gelesenen Codes
|
||||||
gegen einzelne Lichtreflexe auf glänzenden Metalletiketten, die eine reine
|
bereits einem Stapel dieser Sitzung bekannt, steht die Zuordnung ohnehin
|
||||||
Min/Max-Spreizung leicht kippen würden. Das OCR-Ergebnis wird
|
fest — dann entfällt auch die Texterkennung, weil sie nichts mehr beitragen
|
||||||
anschließend gegen die bekannten Spec-Werte abgeglichen, was die
|
könnte, das über die Zuordnung entscheidet. Bei einem tatsächlich neuen
|
||||||
typischen Verwechslungen (0/O, 1/I, 8/B, …) auflöst.
|
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
|
4. **Stapel-Zuweisung.** Grün (Barcode) und Gelb (OCR) laufen ohne Eingabe
|
||||||
durch; nur bei roter Konfidenz (nichts Eindeutiges erkannt oder
|
durch; nur bei roter Konfidenz (nichts Eindeutiges erkannt, widersprüchliche
|
||||||
widersprüchliche Barcodes) oder mehrdeutiger Stapelzuordnung fragt die
|
Barcodes oder widersprüchliche unbekannte Barcodes ohne jede OCR-Kapazität)
|
||||||
App nach.
|
oder mehrdeutiger Stapelzuordnung fragt die App nach.
|
||||||
5. **Sitzungsfortsetzung beim Start.** Findet die App beim Laden eine in
|
5. **Sitzungsfortsetzung beim Start.** Findet die App beim Laden eine in
|
||||||
`localStorage` gesicherte, nicht leere Sitzung vor, wird sie nicht
|
`localStorage` gesicherte, nicht leere Sitzung vor, wird sie nicht
|
||||||
stillschweigend übernommen, sondern zur Fortsetzung oder zum Verwerfen
|
stillschweigend übernommen, sondern zur Fortsetzung oder zum Verwerfen
|
||||||
@@ -96,7 +121,7 @@ korrekt funktioniert.
|
|||||||
npm test
|
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
|
die reinen Module ohne Browser-Zugriff: Teilenummer-Decoder, Spec-Normalisierung
|
||||||
und toleranter Vergleich, OCR-Bildaufbereitung (Otsu-Schwellwert) und
|
und toleranter Vergleich, OCR-Bildaufbereitung (Otsu-Schwellwert) und
|
||||||
-Feldextraktion, Stapel-Zuweisung, die Erkennungs-Pipeline samt Zeitgrenzen
|
-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-tables.js` | Herstellertabellen für den Teilenummer-Decoder |
|
||||||
| `src/pn-decoder.js` | Teilenummer → Spec-Felder |
|
| `src/pn-decoder.js` | Teilenummer → Spec-Felder |
|
||||||
| `src/ocr-extract.js` | OCR-Rohtext → Spec-Felder |
|
| `src/ocr-extract.js` | OCR-Rohtext → Spec-Felder |
|
||||||
| `src/session.js` | Stapel halten, vorschlagen, buchen, umsortieren, zurücknehmen |
|
| `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 und Mehrdeutigkeitsbehandlung |
|
| `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/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/camera.js` | Kamerastart, Einzelbildaufnahme, Bilddatei-Ersatzweg |
|
||||||
| `src/barcode.js` | Adapter zu `zxing-wasm` |
|
| `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
|
Sitzung verworfen (die App startet leer), statt mit einem halb kaputten
|
||||||
Zustand lautlos weiterzuarbeiten. Der gespeicherte Stapelzähler selbst wird
|
Zustand lautlos weiterzuarbeiten. Der gespeicherte Stapelzähler selbst wird
|
||||||
nie übernommen, sondern beim Laden immer neu aus der tatsächlichen Zahl der
|
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
|
## Grenzen
|
||||||
|
|
||||||
@@ -173,7 +202,11 @@ zugeordneten Einträge berechnet.
|
|||||||
- Das Laden der Seite braucht eine Verbindung; das Sortieren selbst nicht
|
- Das Laden der Seite braucht eine Verbindung; das Sortieren selbst nicht
|
||||||
(nach dem Laden läuft alles lokal im Browser).
|
(nach dem Laden läuft alles lokal im Browser).
|
||||||
- Der Teilenummer-Decoder kennt bisher nur das Samsung-DDR4-Schema. Unbekannte
|
- 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.**
|
- **Der Teilenummer-Decoder ist nur teilweise gegen reale Module geprüft.**
|
||||||
Belegt ist ausschließlich der Samsung-Eintrag `A8K40` (64 GB, 4DRx4) mit
|
Belegt ist ausschließlich der Samsung-Eintrag `A8K40` (64 GB, 4DRx4) mit
|
||||||
dem Geschwindigkeitscode `CRC`, anhand des Referenzmoduls
|
dem Geschwindigkeitscode `CRC`, anhand des Referenzmoduls
|
||||||
@@ -259,6 +292,16 @@ werden:
|
|||||||
Etiketten keine tatsächlich benötigten Zeichen ausschließt.
|
Etiketten keine tatsächlich benötigten Zeichen ausschließt.
|
||||||
- Bild ohne lesbaren Barcode und ohne verwertbaren OCR-Text: Rot-Dialog
|
- Bild ohne lesbaren Barcode und ohne verwertbaren OCR-Text: Rot-Dialog
|
||||||
erscheint statt Fehler oder Absturz.
|
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**
|
**Bedienung und Anzeige**
|
||||||
- Zielrahmen liegt sinnvoll über dem Kamerabild, `object-fit: cover` füllt
|
- Zielrahmen liegt sinnvoll über dem Kamerabild, `object-fit: cover` füllt
|
||||||
|
|||||||
Reference in New Issue
Block a user