Skip to content

Ins Logbuch sehen, nicht nur hineinschreiben - #196

Merged
oe5sos merged 1 commit into
mainfrom
feat/handfunke-logbuch-lesen
Oct 5, 2026
Merged

oe5sos merged 1 commit into
mainfrom
feat/handfunke-logbuch-lesen

Conversation

@oe5sos

@oe5sos oe5sos commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Seit #184 kann die App loggen, aber nicht nachsehen. Das ist die schlechtere
Hälfte: ein Eintrag, der danebenging, fällt erst am Pult auf, und „hatte ich
den schon?"
ist die Frage, die in den zwei Sekunden zwischen Rufzeichen und
Anruf beantwortet werden muss — danach ist sie wertlos.

Zuerst gemessen, dann gebaut

An Martins echtem Logbuch (6,6 MB, 9271 Datensätze):

voller Durchlauf 4,1 ms
Suche nach einem Rufzeichen 3,8 ms (3 Treffer)
letzte 64 kB 0,04 ms (78 Datensätze)

Damit fällt der zweite Index weg, den ich bauen wollte. WorkedBefore gehört
ohnehin RotorLogbookPanel — einem Fenster; der Netzdienst darf da nicht
hineingreifen. Ein Index müsste gepflegt und bei jedem fremden Schreibzugriff
verworfen werden; ein Durchlauf von 4 ms kann nicht veralten.

Zwei Befehle

log_last:<n>;   -> je Kontakt log_qso_zeile:<nr>,<datum>,<zeit>,<ruf>,
                   <band>,<mode>,<rst_s>,<rst_r>;  dann log_last_ok:<anzahl>;
log_dup:<ruf>;  -> log_dup_ok:<ruf>,<anzahl>,<datum>,<zeit>,<band>,<mode>,
                   <gleiches band 0|1>,<dupe 0|1>;

Band und Betriebsart für log_dup: kommen aus derselben Stelle wie beim
Eintragen (bandUndModeDerScheibe). Sonst könnte die Antwort „schon
gearbeitet" auf ein Band gehen, das das Gerät nicht eingestellt hat.

Dieselbe Sperre wie beim Schreiben (TciAllowRemoteLog): wer im Logbuch lesen
darf, sieht jedes Rufzeichen und jede Zeit darin.

Die Gefahr, gegen die der Prüfstand steht

LogbuchRueckschau liest vom Dateiende. Ein Schnitt an einer willkürlichen
Bytestelle beginnt mitten in einem Datensatz, und der ADIF-Parser macht aus der
hinteren Hälfte einen eigenen Kontakt. In einer von Longpath geschriebenen
Datei steht CALL zuerst und isValid() fängt das zufällig ab — ADIF schreibt
die Reihenfolge aber nicht vor, und Martins Datei enthält zusammengeführte
Importe. Der Stand führt denselben Schnitt ohne und mit Grenzschnitt vor; ohne
entsteht der erfundene Kontakt wirklich.

Auf der Seite

Die letzten Kontakte stehen im selben Blatt wie die Eingabe: nach dem
Eintragen steht der eigene Kontakt als erste Zeile da. Das ist der Beleg, den
die Erfolgsmeldung nur behaupten kann.

Unter dem Rufzeichenfeld sagt eine Zeile, was das Logbuch weiß — in Messing,
weil es gemessen ist; kräftiges Rot bleibt der Warnung:

DUPE · 20m CW schon gearbeitet · 02.10. 19:12
1× · dieses Band schon, andere Betriebsart · 02.10. 14:30 · 20m SSB
1× · zuletzt 01.10. 08:15 · 40m SSB · dieses Band noch nicht
NEU — noch nie gearbeitet

47 Prüfpunkte

13 (tst_logbuch_rueckschau) + 9 (tst_tci_logbuch_lesen, über den echten
WebSocket) + 25 (pruefe-logbuch.mjs, ohne Browser).

Die Zahl in der Abschlusszeile muss mit den gesendeten Zeilen
übereinstimmen — die Seite zeigt eine Liste nur dann, weil eine kürzere Liste
wie ein kürzeres Logbuch aussieht und niemand es merkt.

Am Telefonformat gefunden, nicht im Code: alle drei Dupe-Zustände kamen in
derselben Farbe heraus. Die Zustandsklasse hieß wie die Grundklasse, und
.dupe.dupe trifft jedes Element mit class="dupe" und schlägt per
Spezifität die beiden anderen Regeln.

🤖 Generated with Claude Code

Seit #184 kann die App loggen, aber nicht nachsehen. Das ist die
schlechtere Haelfte: ein Eintrag, der danebenging, faellt erst am Pult
auf, und "hatte ich den schon?" ist die Frage, die in den zwei Sekunden
zwischen Rufzeichen und Anruf beantwortet werden muss -- danach ist sie
wertlos.

Zwei Longpath-eigene TCI-Befehle, Gegenstueck zu `log_qso:`:

    log_last:<n>;   -> je Kontakt log_qso_zeile:, dann log_last_ok:<n>;
    log_dup:<ruf>;  -> log_dup_ok:<ruf>,<anzahl>,...,<dupe 0|1>;

ZUERST GEMESSEN, dann gebaut. An Martins echtem Logbuch (6,6 MB, 9271
Datensaetze):

    voller Durchlauf              4,1 ms
    Suche nach einem Rufzeichen   3,8 ms   (3 Treffer)
    letzte 64 kB                  0,04 ms  (78 Datensaetze)

Damit faellt der zweite Index weg, den ich bauen wollte. `WorkedBefore`
gehoert ohnehin `RotorLogbookPanel` -- einem Fenster; der Netzdienst
darf da nicht hineingreifen. Ein Index muesste gepflegt und bei jedem
fremden Schreibzugriff verworfen werden; ein Durchlauf von 4 ms kann
nicht veralten.

LogbuchRueckschau liest darum vom DATEIENDE und siebt fuer die
Dupe-Frage nach dem Rufzeichen vor. Genau daraus entsteht die Gefahr,
gegen die der Prueftstand vor allem steht: ein Schnitt an einer
willkuerlichen Bytestelle beginnt mitten in einem Datensatz, und der
ADIF-Parser macht aus der hinteren Haelfte einen eigenen Kontakt. In
einer von Longpath geschriebenen Datei steht CALL zuerst und
`isValid()` faengt das zufaellig ab -- ADIF schreibt die Reihenfolge
aber nicht vor, und Martins Datei enthaelt zusammengefuehrte Importe.
Der Stand fuehrt denselben Schnitt ohne und mit Grenzschnitt vor; ohne
entsteht der erfundene Kontakt wirklich.

Auf der Seite stehen die letzten Kontakte im SELBEN Blatt wie die
Eingabe: nach dem Eintragen steht der eigene Kontakt als erste Zeile
da. Das ist der Beleg, den die Erfolgsmeldung nur behaupten kann.
Darunter dem Rufzeichenfeld sagt eine Zeile, was das Logbuch weiss --
in Messing, weil es gemessen ist; kraeftiges Rot bleibt der Warnung.

Band und Betriebsart fuer `log_dup:` kommen aus derselben Stelle wie
beim Eintragen (bandUndModeDerScheibe). Sonst koennte die Antwort
"schon gearbeitet" auf ein Band gehen, das das Geraet nicht eingestellt
hat.

47 Pruefpunkte: 13 (LogbuchRueckschau) + 9 (TCI, ueber den echten
WebSocket) + 25 (Seite, ohne Browser). Die Zahl in der Abschlusszeile
MUSS mit den gesendeten Zeilen uebereinstimmen -- die Seite zeigt eine
Liste nur dann, weil eine kuerzere Liste wie ein kuerzeres Logbuch
aussieht und niemand es merkt.

Am Telefonformat gefunden, nicht im Code: alle drei Dupe-Zustaende
kamen in derselben Farbe heraus. Die Zustandsklasse hiess wie die
Grundklasse, und `.dupe.dupe` trifft jedes Element mit class="dupe" und
schlaegt per Spezifitaet die beiden anderen Regeln.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@oe5sos
oe5sos merged commit 7a37631 into main Oct 5, 2026
12 checks passed
oe5sos added a commit that referenced this pull request Oct 5, 2026
#196 ging als Quetsch-Commit auf main, der Spots-Zweig trug dieselben
Aenderungen noch als eigenen Commit. Daher sechs Konflikte, alle von
derselben Form: unsere Seite fuegt etwas hinzu, mains Seite ist an der
Stelle leer.

Vor dem Aufloesen nachgerechnet statt angenommen -- jede main-Seite
zaehlt 0 Zeilen, erst danach "unsere behalten". Die Konfliktliste kommt
dabei von `git diff --diff-filter=U` und nicht aus einer selbst
getippten Aufzaehlung: zwei Dateien (CMakeLists.txt, app.js) hatte ich
genau so uebersehen, weil ein `head` die Liste abschnitt.

Danach meldete CMake "Cannot find source file: <<<<<<<". Die
Pruefstaende waren in dem Zustand GRUEN -- mit den alten Binaerdateien,
weil build.ninja sich nicht neu erzeugen konnte. Genau das falsche
Gruen, vor dem docs/development/fast-test-loop.md warnt. Erst neu
konfiguriert, dann gebaut, dann gemessen:

    tst_spot_auswahl         12
    tst_tci_spots             7
    tst_logbuch_rueckschau   13
    tst_tci_logbuch_lesen     9
    pruefe-spots.mjs         33
    pruefe-logbuch.mjs       25

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant