Kennung, deutsche Beschriftung, Codearten, laufende Suche und
Texterkennung je Funktion an einer einzigen Stelle (src/scan-modes.js),
ohne Browser-Zugriff und damit unter Node pruefbar. Funktion "QR-Code"
enthaelt bewusst kein DataMatrix.
FRAME_INSET und frameRect() legen die Rahmenmasse einmalig fest, statt
sie nur im Stilblatt zu duplizieren. grabFrameRegion() liefert den
Zielrahmen unskaliert aus dem Kamerabild - grabFrame() (fuer OCR)
bleibt unveraendert. Ein Code-128 auf kleinem Etikett braucht die
volle Aufloesung, sonst verschmieren die Striche unlesbar.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ein gelesener Rohcode ist nicht garantiert eine Teilenummer: teilen sich
zwei technisch verschiedene Module z.B. denselben Los-Tag, hat der bisherige
Codeabgleich sie ohne Rueckfrage auf denselben Stapel gezogen. proposeAssignment
verlangt fuer einen Codekandidaten jetzt zusaetzlich specsCompatible; widerspricht
der einzige Treffer, faellt die Zuordnung auf den Vergleich der technischen
Angaben zurueck und im Zweifel auf einen eigenen Stapel.
TDD: Test zuerst rot gesehen (Los-Tag-Fall aus der Beanstandung), dann die
Filterung ergaenzt.
Jeder Stapel fuehrt jetzt die Menge der Barcode-Zeichenketten, die bei
seinen Modulen gelesen wurden (stack.codes). Traegt ein gescanntes Modul
einen Code, den ein Stapel bereits kennt, hat das Vorrang vor dem Vergleich
der technischen Angaben - ein Barcode ist exakt gelesen, abgeleitete Angaben
koennen fehlerbehaftet sein. Genau ein Stapel ueber einen bekannten Code
-> Zuweisung, auch bei unvollstaendigen Angaben; mehrere -> weiterhin
mehrdeutig, der Nutzer entscheidet; kein passender Code -> unveraendert die
bisherige Regel. Eine Seriennummer, die bei jedem Modul anders ist, laeuft
dabei einfach ins Leere, ohne die Zuordnung ueber die Teilenummer zu
verhindern.
proposeAssignment und commitAssignment bekommen dafuer einen neuen, optional
en codes-Parameter (Vorgabe []); bestehende Aufrufe ohne diesen Parameter
verhalten sich unveraendert.
storage.js sichert und prueft stack.codes jetzt mit: fehlt das Feld (Stand
aus einer aelteren Fassung), gilt der Stapel als ohne bekannte Codes statt
den ganzen Stand zu verwerfen; ist es vorhanden, muss es eine Liste von
Zeichenketten sein wie jedes andere Feld auch.
Der Teilenummer-Decoder kennt bislang nur das Samsung-Schema; ein Hynix-Modul
mit einwandfrei gelesenem, aber unbekanntem Barcode-Schema endete deshalb in
Rot. Zum Sortieren muss keine Kapazitaet bekannt sein - eine exakt gelesene
Teilenummer genuegt, um ein Modul wiederzuerkennen. recognize() greift jetzt
erst dann auf Rot zurueck, wenn auch die Texterkennung keine Kapazitaet und
keine eindeutige Teilenummer liefert; die bestehende Vorsicht bei
widerspruechlichen unverwertbaren Barcodes bleibt dabei unangetastet.
Zusaetzlich kann recognize() jetzt ueber deps.isKnownCode (optional) erfahren,
dass ein gelesener Code bereits einem Stapel bekannt ist, und die bis zu 20
Sekunden dauernde Texterkennung dann ueberspringen. Ohne diese Abhaengigkeit
verhaelt sich die Pipeline unveraendert. recognize() liefert zusaetzlich die
rohen gelesenen Barcode-Inhalte (codes), damit session.js sie einem Stapel
zuordnen kann.
- Benenne REFERENZ in test/ocr-extract.test.js zu REFERENCE um
(Bezeichner auf Englisch, Kommentare/Texte auf Deutsch)
- Entferne »main«: "index.js" (Datei existiert nicht, nicht relevant für Anwendung)
- Entferne »directories«: { "doc": "docs" } (nicht relevant)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
src/ocr-extract.js: RANK_PATTERN enthielt [Xx], obwohl der Text an dieser
Stelle bereits vollstaendig grossgeschrieben ist (normalizeToken laeuft
vorher) - das kleine x konnte nie ankommen. Auf X reduziert, mit Kommentar
begruendet. Die uebrigen Muster im Modul (CAPACITY_PATTERN, SPEED_PATTERN,
PART_NUMBER_PATTERN, das formFactor-Muster, DATE_CODE_PATTERN) geprueft:
keine weiteren unerreichbaren Zweige, alle nutzen bereits ausschliesslich
Grossbuchstaben-Zeichenklassen bzw. Ziffern.
test/ocr-extract.test.js: Testname behauptete "nur einer plausibel", der
Referenztext enthaelt aber zwei plausible Datumscode-Kandidaten (1234 und
1908) - der Test belegt tatsaechlich, dass bei mehreren plausiblen
Kandidaten der zuletzt vorkommende gewinnt. Umbenannt entsprechend.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
test/storage.test.js: wieder -> reloaded, kaputt -> broken, zweiterEintrag
-> secondEntry, vorschlag -> proposal (Bezeichner, nicht die deutschen
Testbeschreibungen).
Neuer Test deckt die bislang nur mit einem einzigen Stapel geprueften
Stapelzaehler-Neuberechnung beim Laden ab: drei gespeicherte Stapel mit
absichtlich falschem count, davon einer (C) ganz ohne zugeordnete
Eintraege - alle drei muessen unabhaengig voneinander korrekt aus den
tatsaechlichen Eintraegen neu berechnet werden (A=2, B=1, C=0).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Projektvorgabe: Kommentare und Oberflaechentexte deutsch, Bezeichner im Code
englisch. Betroffen: src/barcode.js (vorbereitungsPromise -> readyPromise,
inkl. Verweis-Kommentar in ocr.js), sowie lokale Variablen in
test/spec-match.test.js, test/session.test.js und test/pipeline.test.js
(u.a. ocrAufgerufen -> ocrCalled, gefunden bei vollstaendigem Durchgang
aller Test-Dateien). Testbeschreibungen (Prosa) und Kommentare bleiben
unangetastet - nur echte Bezeichner wurden umbenannt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
proposeAssignment verlangt jetzt zusätzlich zur bisherigen
Verträglichkeitsprüfung, dass Stapel und Modul mindestens ein Merkmal
gemeinsam belegt haben. Ein Stapel mit vollständig leerem Spec (Rot-Fall,
"neuer Stapel") hatte sonst mit jedem Modul null gemeinsame Felder und
galt fälschlich als verträglich mit allem.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ein einzelnes Reflexpixel (Normalfall bei glaenzenden Metalletiketten)
dominierte bisher Minimum/Maximum der Kontrastspreizung und drueckte
Schrift und Untergrund gemeinsam unter den festen Schwellwert 128 - die
Schrift verschwand. Der Schwellwert wird jetzt per Otsu-Verfahren aus dem
Helligkeits-Histogramm des Bildes selbst bestimmt und bleibt dadurch gegen
einzelne Ausreisser unempfindlich. Ausserdem: fehlendes Kleinbuchstaben-x
in der Tesseract-Zeichenliste ergaenzt (Bestueckungsangabe "4DRx4").
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
preprocess() rechnet rein (Graustufen, Kontrastspreizung, Schwellwert) ohne
DOM-Zugriff und ist damit ohne Browser testbar. runOcr() ist der Browser-
Adapter, cacht den Tesseract-Arbeiter und laesst isOcrAvailable() sich nach
einem Fehlschlag wieder erholen statt dauerhaft einzufrieren - analog zum
ensureReady()-Muster in barcode.js.
- Fuegt isPositiveInteger() hinzu fuer Validierung von Eintragsnummern
- nextEntryId und entry.entryId muessen ganze Zahlen >= 1 sein
- Akzeptiert nicht mehr negative, gebrochene oder Null-Werte
- Vier neue Tests pruefen die verstaerkten Anforderungen
- Alle bestehenden Tests bleiben gruen
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stapelzähler wird beim Laden immer aus den zugeordneten Einträgen neu
berechnet statt aus dem gespeicherten Wert übernommen. Zusätzlich wird
verworfen (null): Einträge mit unbekannter Stapelkennung, doppelte
Eintragsnummern, doppelte Stapelkennungen und ein nextEntryId, der nicht
größer als jede vorhandene Eintragsnummer ist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
loadSession pruefte bisher nur, ob stacks/entries Arrays sind, nicht
deren Inhalt. Ein fremder oder verfaelschter Eintrag unter dem
gleichen Schluessel kam dadurch als vermeintlich gueltige Sitzung
durch und liess die App beim naechsten Scan mit TypeError abstuerzen
(stack.spec fehlte). Falsch typisierte Felder (nextEntryId als String,
stack.count als String) wurden ebenfalls durchgereicht und verdarben
die Sitzung lautlos (doppelte entryIds, "3" + 1 = "31").
loadSession verlangt jetzt, dass nextEntryId eine endliche Zahl ist
und dass jeder Stapel/Eintrag ein echtes Objekt (kein Array, nicht
null) mit den Feldern ist, die session.js tatsaechlich weiterverwendet.
Schlaegt eine Pruefung fehl, liefert loadSession null - ein halb
brauchbarer Zustand wird nicht repariert oder teilweise uebernommen.
Mehrere Barcodes ohne bekanntes Nummernschema, die sich in der
Teilenummer widersprechen, durften bisher nicht mehr Vorrang vor der
per OCR gelesenen Teilenummer erhalten als ein einzelner Treffer -
der erste gewann ungeprueft. Jetzt gilt derselbe Massstab wie bei
verwertbaren Treffern: Vorrang nur bei Eindeutigkeit (ein Code, oder
mehrere mit derselben - via canonical() verwechslungstolerant
verglichenen - Teilenummer). Widersprechen sie sich, stuetzt sich das
Ergebnis allein auf die Texterkennung.
Zusaetzlich: ungueltige @param-Zeile fuer deps in recognize() korrigiert.
- Barcode- und OCR-Adapter erhalten je eine eigene, ueberschreibbare
Zeitobergrenze (deps.barcodeTimeoutMs / deps.ocrTimeoutMs). Ein
Adapter, der nie einloest, blockiert recognize() damit nicht mehr
dauerhaft und faellt stattdessen auf den Ersatzwert zurueck, genau
wie bei einem geworfenen Fehler. Kein offener Timer bleibt zurueck.
- mergeCompatibleSpecs entfernt: vertraegliche verwertbare
Barcode-Treffer sind wegen der Kopplung von partNumber und den
uebrigen Feldern ohnehin identisch, der erste Treffer genuegt.
Test entsprechend umbenannt, um das tatsaechlich gepruefte
Verhalten zu benennen statt eine nicht mehr vorhandene
Auffuell-Semantik zu behaupten.
- Kommentar in pn-tables.js von einer inhaltlichen Wiederholung
befreit.
recognize() stuerzte ab, wenn decodeBarcodes() etwas anderes als eine Liste
zurueckgab (null/undefined/Objekt), da die for...of-Schleife nicht iterierbare
Werte nicht abfaengt. Nicht-Listen werden nun wie eine leere Liste behandelt,
ebenso wird eine nicht-string-wertige runOcr()-Rueckgabe wie leerer Text
behandelt.
Ausserdem gewann bei mehreren verwertbaren Barcodes im selben Bild bisher
ungeprueft der erste Treffer mit gruener Ampel. Jetzt werden alle verwertbaren
Codes gesammelt: genau einer bleibt gruen wie bisher, mehrere untereinander
vertraegliche werden zusammengefuehrt (spaeterer Treffer ueberschreibt kein
bereits gesetztes Feld) und bleiben gruen, mehrere unvertraegliche ergeben rot
mit leerem Spec statt eines geratenen Ergebnisses.
Korrigiert ausserdem den irrefuehrenden Warnkommentar in pn-tables.js: ein
falscher Tabelleneintrag faellt auf dem Barcode-Weg nicht automatisch auf, da
dort keine Texterkennung zum Abgleich laeuft.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commitAssignment and moveEntry stored the same spec object in both
stack.spec and entry.spec. A later, more complete scan enriching
stack.spec would retroactively mutate the entry that first created the
stack, making it appear as if it had been scanned with data it never
actually had. Both places now store independent shallow copies so the
entries log and the stacks spec (comparison baseline) can't leak into
each other.
Also documents that undoLast intentionally reverts the last *recorded*
entry, not the last *action* - a subsequent moveEntry/removeEntry on a
different entry does not change what undoLast will take back - and
adds a test pinning that contract after a reorder.
Part number now picks the longest hyphenated candidate instead of the
first one, matching the documented intent and preventing a short
unrelated token from being reported as the part number (which feeds
the fingerprint used for sorting).
Date code now only accepts YYWW candidates with a plausible year
(10-39) and valid calendar week (01-53), taking the last match when
several qualify; otherwise it stays null instead of guessing wrong,
since a wrong value is worse than a missing one.
Adds regression tests for both cases plus a no-plausible-candidate
case that must yield null.
The lazy quantifier {4,6}? never expanded beyond its minimum because
the trailing greedy [A-Z0-9]* always absorbed the rest up to the
hyphen, so a longer density code would silently be truncated to 5
chars and could match an unrelated table entry, producing wrong specs
instead of null. The density code is actually fixed-width (A + 4
chars); the revision after it is the variable part. Tighten the
pattern to {4}, rewrite the stale comment (which still claimed a fixed
3-char revision), and add a test that every Samsung density key is
exactly 5 characters so a future mismatched table entry can't silently
become unreachable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Das Samsung-Muster hat die Revision auf exakt 3 Zeichen festgelegt und die
Dichte-Gruppe auf exakt 4 Folgezeichen. Dies führte dazu, dass bei
abweichender Revisionslänge das gesamte Muster nicht passte und Bauform,
Geschwindigkeit und Kapazität verloren gingen.
Das neue Muster verwendet einen faul (lazy) Quantifier für die
Dichte-Gruppe (4-6 Zeichen) und erlaubt beliebig lange Revisionen vor
dem Bindestrich. Dadurch werden Bauform und Geschwindigkeit auch bei
nicht standardisierten Revisionslängen korrekt erkannt.
- M393A2K43B-CTD (einteilige Revision)
- M393A2K43BBX1-CTD (vierteilige Revision)
Beide Tests bestätigen jetzt die korrekte Erkennung von Bauform und
Geschwindigkeit unabhängig von der Revisionslänge.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deckt die Samsung-Referenz-Teilenummer aus dem physisch vorhandenen
Modul ab. Unbekannte Fragmente (z.B. nicht gelistete Dichte-Codes)
liefern bewusst null statt einer Vermutung - das ist der geplante
Uebergang zum OCR-Weg, kein Fehlerfall.
Das im Task-Brief vorgegebene Regex-Muster liess das literale 'A' der
Dichte-Kennung ausserhalb der Erfassungsgruppe, wodurch die
Tabellensuche nach 'A8K40' nie greifen konnte. Muster korrigiert, damit
die ebenfalls vorgegebenen Tests bestehen; Tabelleninhalte und
Warnkommentar blieben wortgleich.
canonical() und matchKnown() stützen sich auf normalizeToken(), das bei
Nicht-Strings '' zurückgibt. Dies führt dazu, dass bereits geparste Zahlenwerte
lautlos ignoriert werden: canonical(64) liefert '' statt '64', matchKnown(64, list)
liefert null statt den gefundenen Wert.
Die Behebung konvertiert Zahlen zu Zeichenketten, bevor sie normalizeToken()
erreichen, ohne null/undefined zu ändern (diese ergeben weiterhin ''/'null).
Alle 23 Tests grün.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>