mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-09-10 19:25:32 +02:00
Compare commits
4
Commits
b550d79b4b
...
21b2d9970a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21b2d9970a | ||
|
|
94fc13e3c3 | ||
|
|
d475c6b2c4 | ||
|
|
211be6081d |
+22
-4
@@ -1,6 +1,6 @@
|
||||
# KST4Contest Project Context
|
||||
|
||||
Last reviewed: 2026-08-27
|
||||
Last reviewed: 2026-08-28
|
||||
|
||||
This file is the durable technical project context for KST4Contest. It is not a user manual and not a replacement for the changelog. Current code, tests and authoritative external specifications remain the source of truth when this document is stale or ambiguous.
|
||||
|
||||
@@ -21,8 +21,8 @@ KST4Contest is a Java/JavaFX desktop client for ON4KST chat focused on VHF/UHF/m
|
||||
|
||||
### Chat identity
|
||||
|
||||
- Full callsign variants can be distinct chat-member identities.
|
||||
- Category is part of chat identity.
|
||||
- For remote chat participants, the complete visible callsign plus category forms the chat-member identity.
|
||||
- Full callsign variants can therefore be distinct chat-member identities.
|
||||
- Base-call normalization is permitted only for explicitly base-call-wide functions.
|
||||
- Worked status is shared across suffix variants of the same base call.
|
||||
- Monitoring a variant such as `DN9APW-2` or `DN9APW-70` intentionally monitors the base call `DN9APW`.
|
||||
@@ -65,10 +65,28 @@ Known integration areas include:
|
||||
|
||||
CR/LF framing, XML framing, ports/transports, callsign normalization and frequency formatting are protocol behaviour and must not be changed as incidental cleanup.
|
||||
|
||||
### Logging and Worked-state persistence
|
||||
|
||||
- The Simplelogfile interpreter reads the selected text file after connection startup and then once per minute using a fixed built-in callsign pattern.
|
||||
- Simplelogfile callsigns are normalized to base callsigns and set only the global Worked state for every active variant. They do not create per-band Worked or grid-square state.
|
||||
- Simplelogfile-derived Worked state is not persisted in SQLite. The selected file is the durable source and is read again in each application session.
|
||||
- The interpreter only adds positive runtime marks. It does not remove existing marks during the current session and does not reset automatically when a new contest starts.
|
||||
- A missing selected file is created. Read, path and creation failures are contained so the periodic timer remains alive; successful creation triggers a one-time, non-blocking UI notice with the exact path and setup/contest checks.
|
||||
- Network-derived and manually assigned Worked, NOT-QRV and worked-grid state continues to use SQLite with its established lifetime and reset behaviour.
|
||||
- Automatic QRG updates require both an enabled source and valid incoming `RadioInfo` or Win-Test `STATUS` data. Merely enabling a source does not provide or validate a current QRG.
|
||||
|
||||
### ON4KST session and authentication
|
||||
|
||||
- One KST4Contest connection authenticates one ON4KST TCP session with one local login callsign and one password.
|
||||
- The TCP session uses one common locator for both categories; the locator is not part of authentication.
|
||||
- The primary category is part of the initial login. A distinct second category is added to the same session through ON4KST Single Sign-on; it must not create a second TCP connection or local login.
|
||||
- **Name in Chat** is a visible category-specific name field, not a login callsign or message destination. Private messages to the local station are addressed to the local login callsign.
|
||||
- The visible **Name in Chat** field, message context, QRG and beacon configuration remain category-specific.
|
||||
|
||||
### ON4KST session liveness
|
||||
|
||||
- After 90 seconds without inbound data, the application keeps the established empty CRLF heartbeat.
|
||||
- At about 180 seconds of inbound idle time, the TCP session sends one `RDXQ|<main chat id>|` probe. The probe state belongs to the session, so a two-category login still sends only one probe per idle phase.
|
||||
- At about 180 seconds of inbound idle time, the TCP session sends one `RDXQ|<main chat id>|` probe. The probe state belongs to the session, so a two-category session still sends only one probe per idle phase.
|
||||
- Any subsequent inbound server frame confirms the probe. `DXQ` is accepted as the expected internal response and is not published as chat content.
|
||||
- If no inbound frame arrives by about 210 seconds, the existing reconnect flow remains responsible for replacing the session.
|
||||
- Probe diagnostics contain the session id, main category, opcode and timing only. They must not include credentials, complete server frames or normal chat messages.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Verbinden mit dem Chat
|
||||
|
||||
Vor dem ersten Verbindungsaufbau müssen im Einstellungsfenster mindestens Rufzeichen, Passwort, Locator und primäre Chat-Kategorie konfiguriert werden. Soll zusätzlich eine zweite Kategorie verwendet werden, muss auch deren Login aktiviert und vollständig eingerichtet sein.
|
||||
Vor dem ersten Verbindungsaufbau müssen im Einstellungsfenster das lokale Login-Rufzeichen und Passwort, der gemeinsame Locator der TCP-Sitzung sowie die primäre Chat-Kategorie konfiguriert werden. Soll zusätzlich eine zweite Kategorie verwendet werden, wird sie dort aktiviert und ausgewählt. KST4Contest meldet sich einmal an und ergänzt die zweite Kategorie per Single Sign-on innerhalb derselben TCP-Sitzung.
|
||||
|
||||
Die Verbindung kann auf zwei Wegen aufgebaut werden:
|
||||
|
||||
@@ -54,7 +54,7 @@ Beide Hinweise blinken ungefähr zwölf Sekunden und verschwinden anschließend
|
||||
|
||||
### PM-Fenster (oben links)
|
||||
|
||||
Das PM-Fenster zeigt die an die eigenen Chat-Logins gerichteten Privatnachrichten und die zugehörigen ausgehenden Antworten.
|
||||
Das PM-Fenster zeigt die an das lokale Login-Rufzeichen adressierten Privatnachrichten und die zugehörigen ausgehenden Antworten.
|
||||
|
||||
Nicht selbst gesendete Nachrichten erscheinen dort zusätzlich, wenn ihr Text ohne Beachtung der Groß- und Kleinschreibung das konfigurierte eigene Login-Rufzeichen enthält. Das gilt für öffentliche Nachrichten an `ALL` ebenso wie für gerichtete Nachrichten zwischen anderen Chatteilnehmern. Gerade im zweiten Fall lässt sich dieses PM-Catching flapsig als **„Lästererkennung“** bezeichnen.
|
||||
|
||||
|
||||
@@ -80,6 +80,10 @@ v1.42 führt mehrere bisher getrennte Auswertungen zusammen. Bandinformationen,
|
||||
|
||||
- **DXLog-Gesamtlog übernommen:** Der UCXLog-kompatible UDP-Listener verarbeitet neben `contactinfo` auch `contactreplace`. Dadurch kann ein von DXLog.net als vollständiges Log ausgesendeter Datenbestand eingelesen werden.
|
||||
|
||||
- **Simplelogfile-Verhalten präzisiert:** Die ausgewählte Textdatei wird einmal pro Minute mit einem festen Rufzeichenmuster ausgewertet. Treffer setzen den globalen Worked-Status aller aktiven Varianten des Basisrufzeichens, werden aber nicht in SQLite persistiert. Eine fehlende Datei wird angelegt; Lese- und Erstellungsfehler beenden die periodische Auswertung nicht.
|
||||
|
||||
- **Automatische QRG-Übernahme abgesichert:** `MYQRG` wird nur von einer aktivierten Schnittstelle aktualisiert, die tatsächlich gültige `RadioInfo`- beziehungsweise Win-Test-`STATUS`-Pakete liefert. Eine aktivierte, aber nicht liefernde Quelle ersetzt die notwendige Funktionsprüfung oder manuelle QRG-Pflege nicht.
|
||||
|
||||
- **Versionserkennung verbessert:** Versionsnummern werden semantisch verglichen, damit beispielsweise Patch-Versionen und Nightly-Stände nicht mehr durch eine einfache Fließkommazahl falsch eingeordnet werden.
|
||||
|
||||
### Behoben
|
||||
|
||||
@@ -67,6 +67,8 @@ Aktiviere nur die Schnittstellen, die tatsächlich verwendet und vorher getestet
|
||||
|
||||
Ein Contest ist ein ungünstiger Zeitpunkt, um gleichzeitig die Funkbedingungen und eine erstmals aktivierte Netzwerkschnittstelle zu untersuchen.
|
||||
|
||||
Beim Simplelogfile sollte vor jedem Contest geprüft werden, ob das Logprogramm die aktuelle Contestdatei an den in KST4Contest ausgewählten Pfad schreibt. Ein Test-QSO muss innerhalb einer Minute als global gearbeitet erscheinen. KST4Contest setzt die aus dieser Datei abgeleiteten Worked-Markierungen beim Contestwechsel nicht automatisch zurück.
|
||||
|
||||
---
|
||||
|
||||
## Grundablauf während des Contests
|
||||
@@ -89,7 +91,7 @@ KST4Contest hält die benötigten Informationen zwischen diesen Schritten zusamm
|
||||
|
||||
## CQ-Betrieb
|
||||
|
||||
Bei einer weitgehend festen CQ-QRG sollten `MYQRG` beziehungsweise `SECONDQRG` den tatsächlich verwendeten Frequenzen entsprechen. Eine aktivierte TRX-Synchronisation kann `MYQRG` automatisch aktualisieren. Ohne automatische Quelle muss der Wert von Hand gepflegt werden.
|
||||
Bei einer weitgehend festen CQ-QRG sollten `MYQRG` beziehungsweise `SECONDQRG` den tatsächlich verwendeten Frequenzen entsprechen. Eine aktivierte und tatsächlich liefernde TRX-Synchronisation kann `MYQRG` automatisch aktualisieren. Die Aktivierung allein reicht nicht: Vor dem Contest sollte mit einer realen Frequenzänderung geprüft werden, ob gültige Pakete ankommen. Ohne funktionierende automatische Quelle muss der Wert von Hand gepflegt werden.
|
||||
|
||||
Der Beacon kann die aktuelle QRG, den Locator und die Antennenrichtung regelmäßig im Chat veröffentlichen. Seine Variablen werden bei jedem Sendedurchlauf erneut ausgewertet.
|
||||
|
||||
@@ -257,7 +259,7 @@ Eine weitere, besonders konsequente Betriebsweise nutzt die Stationskarte als ge
|
||||
|
||||
Die Karte wird so zu einer räumlichen Liste der noch abzuarbeitenden Stationen. Dieser Ablauf ist optional. Er setzt eine zuverlässig eingerichtete Log-Synchronisation und DX-Cluster-Verbindung voraus und ist vor allem für Operatoren interessant, die den Contest bewusst auf diese geografische Weise strukturieren möchten.
|
||||
|
||||
[G1YBB zeigt diesen Workflow im Video.](https://www.youtube.com/watch?v=BCNCjowPgec)
|
||||
[G1YBB zeigt diesen Workflow im Video.](https://www.youtube.com/watch?v=lMQZMiSHlUI)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ Diese Trennung ist notwendig. Ein bereits gearbeitetes Rufzeichen kann auf einem
|
||||
|
||||
Die [Log-Synchronisation](de-Log-Synchronisation) übernimmt neue QSOs aus dem Logprogramm. Welche Informationen dabei zur Verfügung stehen, hängt von der verwendeten Schnittstelle ab:
|
||||
|
||||
- Der dateibasierte Simplelogfile-Interpreter erkennt nur das Rufzeichen. Er kann deshalb lediglich den globalen Worked-Status setzen.
|
||||
- Der dateibasierte Simplelogfile-Interpreter liest die ausgewählte Textdatei einmal pro Minute mit einem festen Rufzeichenmuster. Treffer werden auf das Basisrufzeichen normalisiert und setzen für alle aktiven Varianten lediglich den globalen Worked-Status.
|
||||
- Die QSO-UDP-Schnittstellen und der Win-Test-Netzwerk-Listener können zusätzlich das Band übernehmen.
|
||||
- Enthält das Logpaket einen gültigen Locator, speichert KST4Contest außerdem das gearbeitete vierstellige Großfeld für dieses Band.
|
||||
|
||||
@@ -205,7 +205,9 @@ Im Klartext: Ein erkannter Hinweis bedeutet „wahrscheinlich auf diesem Band ak
|
||||
|
||||
### Speicherung und Lebensdauer
|
||||
|
||||
Worked-, NOT-QRV- und Großfeldinformationen werden in der internen SQLite-Datenbank gespeichert und beim nächsten Start wieder geladen. Die Einträge laufen nach drei Tagen automatisch ab. Ein Reset vor jedem Contest ist deshalb normalerweise nicht erforderlich.
|
||||
Worked-, NOT-QRV- und Großfeldinformationen aus den Netzwerkschnittstellen sowie manuelle Markierungen werden in der internen SQLite-Datenbank gespeichert und beim nächsten Start wieder geladen. Die Einträge laufen nach drei Tagen automatisch ab. Ein Reset vor jedem Contest ist deshalb normalerweise nicht erforderlich.
|
||||
|
||||
Simplelogfile-Treffer sind davon ausgenommen. Sie werden nicht in SQLite persistiert, sondern bei jedem Programmlauf aus der ausgewählten Datei abgeleitet. Die Datei ist damit die dauerhafte Quelle. Der Interpreter entfernt während der laufenden Sitzung keine bereits gesetzte Worked-Markierung und setzt den Zustand beim Wechsel zu einem neuen Contest nicht automatisch zurück.
|
||||
|
||||
Ein manueller Reset unter **Workedstn database** entfernt sämtliche Worked-Markierungen, NOT-QRV-Tags und gespeicherten Worked-Großfelder. Die bekannten Rufzeichenzeilen bleiben dabei in der Datenbank erhalten. Einzelheiten: [Worked Station Database Settings](de-Konfiguration#worked-station-database-settings-gearbeitete-stationen-datenbank).
|
||||
|
||||
@@ -308,7 +310,7 @@ Konfiguration, erkannte QRG-Anfragen und genaue Kategorienzuordnung: [Konfigurat
|
||||
|
||||
## Multi-Channel-Login (ab v1.26)
|
||||
|
||||
Gleichzeitiger Login in **zwei Chat-Kategorien** (z. B. 144 MHz und 432 MHz). Beide Chats werden parallel überwacht.
|
||||
KST4Contest meldet sich mit einem lokalen Login-Rufzeichen und Passwort einmal bei ON4KST an. Ein gemeinsamer Locator gilt für die gesamte TCP-Sitzung. Eine zweite Chat-Kategorie (z. B. 144 MHz und 432 MHz) wird per Single Sign-on innerhalb derselben Sitzung ergänzt. Beide Kategorien werden parallel überwacht; das sichtbare kategoriebasierte Namensfeld, der Nachrichtenkontext, die QRG und der Beacon bleiben je Kategorie getrennt.
|
||||
|
||||
---
|
||||
|
||||
@@ -322,7 +324,7 @@ Die grüne Altersskala der Privatnachrichten bleibt in beiden Darstellungen erha
|
||||
|
||||
## Opposite Station Multi-Callsign Login-Tagging (ab v1.26)
|
||||
|
||||
Unterstützung für Stationen, die mit mehreren Rufzeichen gleichzeitig im Chat aktiv sind (z. B. Expedition-Setups).
|
||||
Unterstützung für Gegenstationen, die mit mehreren vollständigen sichtbaren Rufzeichenvarianten gleichzeitig im Chat aktiv sind (z. B. Expedition-Setups). Vollständiges Rufzeichen und Chat-Kategorie bleiben getrennte Teilnehmeridentitäten; Worked-, Band- und Prioritätsdaten werden über das Basisrufzeichen gemeinsam ausgewertet.
|
||||
|
||||
---
|
||||
|
||||
@@ -438,7 +440,7 @@ Sniffed: (DN9APW-70 > 9A0BB-23) pse sked 19:30
|
||||
|
||||
Das ist ein wichtiger Unterschied. Das Monitoring fasst die Varianten zusammen, damit keine Nachricht übersehen wird. Das Nachrichtenrouting fasst sie nicht zusammen, weil andernfalls nicht mehr eindeutig wäre, welcher Login tatsächlich angesprochen wurde.
|
||||
|
||||
Das Basisrufzeichen wird außerdem unabhängig von der Chat-Kategorie ausgewertet. Ist KST4Contest gleichzeitig mit zwei Kategorien verbunden, gilt derselbe Monitoring-Eintrag für beide Nachrichtenströme. Die Kategorie der einzelnen Nachricht bleibt dabei erhalten.
|
||||
Das Basisrufzeichen wird außerdem unabhängig von der Chat-Kategorie ausgewertet. Sind in derselben ON4KST-Sitzung zwei Kategorien aktiv, gilt derselbe Monitoring-Eintrag für beide Nachrichtenströme. Die Kategorie der einzelnen Nachricht bleibt dabei erhalten.
|
||||
|
||||
Erfasst werden:
|
||||
|
||||
@@ -604,7 +606,7 @@ Die Bewertung wird für das normalisierte Basisrufzeichen vorgenommen. Ein Sked
|
||||
|
||||
### Wie werden mehrere SSIDs und Chat-Kategorien behandelt?
|
||||
|
||||
Aktive Rufzeichen wie `9A0BB-2`, `9A0BB-70`, `9A0BB-23` und `9A0BB-13` bleiben getrennte Chatmember. Dadurch können Nachrichten weiterhin an das vollständige Rufzeichen und die richtige Chat-Kategorie adressiert werden.
|
||||
Aktive vollständige sichtbare Rufzeichenvarianten wie `9A0BB-2`, `9A0BB-70`, `9A0BB-23` und `9A0BB-13` bleiben getrennte Chatmember. Dadurch können Nachrichten weiterhin an das vollständige Rufzeichen und die richtige Chat-Kategorie adressiert werden.
|
||||
|
||||
Worked-, Band-, NOT-QRV- und Score-Informationen beziehen sich dagegen auf das gemeinsame Basisrufzeichen `9A0BB`. Der Score wird deshalb einmal berechnet und auf alle aktiven Varianten übertragen. Die Benutzerliste kann mehrere getrennte Zeilen mit demselben Score enthalten; in der Prioritätsliste erscheint das Basisrufzeichen nur einmal.
|
||||
|
||||
@@ -703,7 +705,7 @@ Die Timeline ist eine Vorschau. AirScout-Daten können sich ändern, und ein ein
|
||||
|
||||
KST4Contest kann wiederkehrende CQ-Nachrichten in den öffentlichen Chat senden. Der Beacon ist für längeres CQ-Rufen auf einer festen Frequenz gedacht: Die eigene QRG wird regelmäßig veröffentlicht, ohne dass derselbe Text von Hand wiederholt werden muss.
|
||||
|
||||
Beide Chat-Kategorien verwenden ein gemeinsames Intervall, besitzen aber jeweils einen eigenen Aktivierungsschalter und Nachrichtentext. Der zweite Beacon wird nur gesendet, wenn auch der zweite Chat aktiviert und verbunden ist.
|
||||
Beide Chat-Kategorien verwenden ein gemeinsames Intervall, besitzen aber jeweils einen eigenen Aktivierungsschalter und Nachrichtentext. Der zweite Beacon wird nur gesendet, wenn die zweite Kategorie in derselben ON4KST-Sitzung aktiviert und vollständig synchronisiert ist.
|
||||
|
||||
Globale Variablen wie `MYQRG`, `SECONDQRG`, `MYLOCATOR` oder `MYQTF` werden unmittelbar vor jeder Aussendung neu aufgelöst. Eine zwischenzeitlich vom Logprogramm aktualisierte QRG kann dadurch bereits im nächsten Beacon erscheinen.
|
||||
|
||||
@@ -718,7 +720,7 @@ Konfiguration, Timer-Verhalten und verfügbare Variablen: [Konfiguration – Bea
|
||||
|
||||
## Simplelogfile
|
||||
|
||||
Details: [Log-Synchronisation](de-Log-Synchronisation#methode-1-universal-file-based-callsign-interpreter-simplelogfile).
|
||||
Der Simplelogfile-Interpreter liest einmal pro Minute Rufzeichen aus einer ausgewählten Textdatei. Fehlt die Datei, legt KST4Contest sie an und zeigt einen nicht blockierenden Hinweis mit Pfad, Funktionstest und Contestprüfung. Details: [Log-Synchronisation](de-Log-Synchronisation#methode-1-universal-file-based-callsign-interpreter-simplelogfile).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -14,14 +14,15 @@ Nach dem ersten Start öffnet sich das **Einstellungsfenster** – dieses ist de
|
||||
|
||||
### Login und Chat-Kategorien
|
||||
|
||||
Hier werden die Zugangsdaten für den ON4KST-Chat eingetragen (Rufzeichen und Passwort).
|
||||
Zudem wird die **primäre Chat-Kategorie** (z. B. IARU Region 1 VHF/Microwave) ausgewählt.
|
||||
Hier werden das lokale Login-Rufzeichen und das Passwort für den ON4KST-Chat eingetragen. Zudem wird die **primäre Chat-Kategorie** (z. B. IARU Region 1 VHF/Microwave) ausgewählt.
|
||||
|
||||
Mit der Option für einen **zweiten Chat** (Multi-Channel-Login) kann man sich gleichzeitig in eine weitere Kategorie (z. B. UHF/SHF) einloggen. Beide Chats werden dann parallel überwacht. Hier kann optional auch ein abweichender Login-Name für den zweiten Chat vergeben werden (nützlich für Opposite Station Multi-Callsign Logging).
|
||||
Mit der Option für einen **zweiten Chat** (Multi-Channel-Login) wird eine weitere Kategorie (z. B. UHF/SHF) per Single Sign-on in derselben ON4KST-TCP-Sitzung aktiviert. KST4Contest öffnet dafür keine zweite TCP-Verbindung und verwendet weder ein zweites lokales Login-Rufzeichen noch ein zweites Passwort.
|
||||
|
||||
**Name in Chat 2** konfiguriert ausschließlich das sichtbare, kategoriebasierte Namensfeld der zweiten Kategorie. Es ist weder ein zweites Login noch ein Nachrichtenempfänger. Das sichtbare Namensfeld, der Nachrichtenkontext, die QRG und der Beacon bleiben für beide Kategorien getrennt. **Opposite Station Multi-Callsign Login-Tagging** bezieht sich dagegen ausschließlich auf Gegenstationen, die mit mehreren vollständigen sichtbaren Rufzeichenvarianten im Chat erscheinen.
|
||||
|
||||
### Rufzeichen und Locator
|
||||
|
||||
Eigenes Rufzeichen und Maidenhead-Locator (6-stellig, z. B. `JN49IJ`) eintragen. Diese Werte werden für Distanz- und Richtungsberechnungen benötigt.
|
||||
Eigenes Rufzeichen und Maidenhead-Locator (6-stellig, z. B. `JN49IJ`) eintragen. Der Locator ist kein Bestandteil der Authentifizierung. ON4KST verwendet ihn gemeinsam für beide Kategorien der einen TCP-Sitzung; KST4Contest benötigt ihn außerdem für Distanz- und Richtungsberechnungen.
|
||||
|
||||
### Aktivierte Bänder
|
||||
|
||||
@@ -108,7 +109,9 @@ Drei Methoden stehen zur Verfügung, um gearbeitete Stationen automatisch zu mar
|
||||
|
||||
### Universal File Based Callsign Interpreter (Simplelogfile)
|
||||
|
||||
Interpretiert beliebige Log-Dateien per Regex nach Rufzeichen-Mustern. Keine Bandinformation möglich. Geeignet als Fallback oder für nicht direkt unterstützte Logprogramme.
|
||||
Liest die ausgewählte Textdatei einmal pro Minute mit einem fest eingebauten Rufzeichenmuster. Die Treffer werden auf Basisrufzeichen normalisiert und setzen für alle aktiven Varianten nur den globalen Worked-Status. Band- und Locatorinformationen können daraus nicht abgeleitet werden. Die Funktion eignet sich als Fallback für Logprogramme ohne unterstützte Netzwerkschnittstelle.
|
||||
|
||||
Existiert die ausgewählte Datei noch nicht, legt KST4Contest sie an und zeigt einmalig den vollständigen Pfad sowie Hinweise zum Funktions- und Contesttest. Die Datei ist selbst die dauerhafte Quelle; aus ihr abgeleitete Worked-Markierungen werden nicht in SQLite gespeichert und nicht automatisch für einen neuen Contest zurückgesetzt.
|
||||
|
||||
### Netzwerk-Listener für QSO-UDP-Broadcast
|
||||
|
||||
@@ -134,7 +137,7 @@ Die TRX-Synchronisation übernimmt die aktuelle Frequenz aus dem Logprogramm und
|
||||
| **Win-Test STATUS** | `Win-Test STATUS QRG Sync` | Verarbeitet die Haupt- oder Pass-Frequenz aus nativen Win-Test-`STATUS`-Paketen. Der Win-Test-Listener verwendet seinen separat konfigurierten Port, standardmäßig `9871`. |
|
||||
| **Manuelle Eingabe** | Beide automatischen QRG-Quellen deaktivieren | Die eigene QRG kann im Hauptfenster von Hand eingetragen werden. |
|
||||
|
||||
Der allgemeine Listener ist für Logprogramme vorgesehen, die kompatible `RadioInfo`-Pakete senden. Dazu gehören – abhängig von deren jeweiliger Konfiguration – UCXLog, N1MM+, QARTest und DXLog.net. QSO- und `RadioInfo`-Pakete verwenden denselben unter **Log sync** konfigurierten Port. Dort wird jedoch getrennt festgelegt, ob KST4Contest QSO-Informationen, TRX-Informationen oder beide Paketarten verarbeitet.
|
||||
Der allgemeine Listener ist für Logprogramme vorgesehen, die kompatible `RadioInfo`-Pakete senden. Dazu gehören – abhängig von deren jeweiliger Konfiguration – UCXLog, N1MM+, QARTest und DXLog.net. QSO- und `RadioInfo`-Pakete verwenden denselben unter **Log sync** konfigurierten Port. Dort wird jedoch getrennt festgelegt, ob KST4Contest QSO-Informationen, TRX-Informationen oder beide Paketarten verarbeitet. Eine automatische QRG-Quelle muss aktiviert sein und tatsächlich gültige Pakete liefern; die Aktivierung allein aktualisiert `MYQRG` nicht.
|
||||
|
||||
Wird der gemeinsame UDP-Port geändert, muss KST4Contest neu gestartet werden. Eine reine Änderung der Checkboxen wird dagegen sofort berücksichtigt.
|
||||
|
||||
@@ -144,7 +147,7 @@ Beide automatischen Quellen aktualisieren ausschließlich `MYQRG`. Das ist die e
|
||||
|
||||
Bei aktiviertem zweiten Chat bleibt dessen QRG davon unabhängig. Sie wird nicht aus den empfangenen TRX-Paketen abgeleitet und steht als `SECONDQRG` zur Verfügung. Dadurch kann beispielsweise die erste Kategorie automatisch der Frequenz des Logprogramms folgen, während für die zweite Kategorie eine eigene QRG von Hand eingetragen wird.
|
||||
|
||||
Sobald mindestens eine automatische QRG-Quelle aktiviert ist, wird das QRG-Feld der ersten Kategorie im Hauptfenster an den empfangenen Wert gebunden. Eine manuelle Eingabe in dieses Feld ist wieder möglich, wenn sowohl der allgemeine RadioInfo-Listener als auch die Win-Test-STATUS-Synchronisation deaktiviert sind.
|
||||
Sobald mindestens eine automatische QRG-Quelle aktiviert ist und einen gültigen Wert liefert, folgt das QRG-Feld der ersten Kategorie im Hauptfenster der empfangenen Frequenz. Bleiben die erwarteten Pakete aus, sollte die Schnittstelle vor dem Contest geprüft werden. Eine manuelle Eingabe in dieses Feld ist möglich, wenn sowohl der allgemeine RadioInfo-Listener als auch die Win-Test-STATUS-Synchronisation deaktiviert sind.
|
||||
|
||||
### Haupt- oder Pass-Frequenz aus Win-Test
|
||||
|
||||
@@ -576,7 +579,7 @@ KST4Contest verwendet einen gemeinsamen Timer für beide Chat-Kategorien. Aktivi
|
||||
- **Beacon message** enthält den öffentlichen Nachrichtentext dieser Kategorie.
|
||||
- **Shared beacon interval** legt das gemeinsame Intervall für beide Kategorien fest.
|
||||
|
||||
Sind beide Beacons aktiviert, werden sie beim selben Timer-Lauf nacheinander in ihren jeweiligen Kategorien gesendet. Der zweite Beacon wird nur berücksichtigt, wenn auch der zweite Chat aktiviert und verbunden ist.
|
||||
Sind beide Beacons aktiviert, werden sie beim selben Timer-Lauf nacheinander in ihren jeweiligen Kategorien gesendet. Der zweite Beacon wird nur berücksichtigt, wenn die zweite Kategorie in derselben ON4KST-Sitzung aktiviert und vollständig synchronisiert ist.
|
||||
|
||||
### Intervall und Timer-Verhalten
|
||||
|
||||
@@ -859,6 +862,8 @@ Die interne SQLite-Datenbank speichert die contestbezogenen Zustände unabhängi
|
||||
- manuell gesetzte NOT-QRV-Tags pro Band und
|
||||
- gearbeitete vierstellige Großfelder pro Band.
|
||||
|
||||
Eine Ausnahme bilden Worked-Markierungen des Simplelogfile-Interpreters. Sie werden nur aus der ausgewählten Datei in den laufenden Zustand übernommen und nicht in SQLite persistiert. Die Datei ist die dauerhafte Quelle und besitzt keinen automatischen Contest-Reset.
|
||||
|
||||
Als Schlüssel wird das normalisierte Rufzeichen ohne sichtbare Chat-Klammern oder Kategorieformatierung verwendet. Dadurch können aktive Varianten desselben Rufzeichens konsistent ausgewertet werden.
|
||||
|
||||
Worked- und NOT-QRV-Informationen laufen drei Tage nach ihrer letzten Änderung automatisch ab. Gespeicherte Großfelder laufen drei Tage nach dem zugehörigen Logeintrag ab. Ein manuelles Zurücksetzen vor jedem Contest ist deshalb normalerweise nicht erforderlich.
|
||||
|
||||
@@ -10,13 +10,15 @@ KST4Contest übernimmt gearbeitete Stationen aus dem Logprogramm und stellt dara
|
||||
|
||||
## Methode 1: Universal File Based Callsign Interpreter (Simplelogfile)
|
||||
|
||||
KST4Contest liest eine Logdatei und sucht mit einem konfigurierbaren regulären Ausdruck nach Rufzeichen. Die Datei wird ausschließlich gelesen und nicht verändert. Auch binäre Logdateien können verwendet werden; nicht als Text interpretierbare Inhalte werden übersprungen.
|
||||
KST4Contest liest die ausgewählte Textdatei einmal pro Minute und sucht darin mit einem fest eingebauten regulären Ausdruck nach Rufzeichen. Jedes gefundene Rufzeichen wird auf sein Basisrufzeichen normalisiert. Der globale Worked-Status gilt dadurch für alle derzeit aktiven Chat-Varianten dieses Rufzeichens.
|
||||
|
||||
Der Vorteil liegt in der breiten Kompatibilität: Die Funktion benötigt keine besondere Netzwerkschnittstelle des Logprogramms.
|
||||
|
||||
Die Grenze ist ebenso eindeutig: Aus einem reinen Rufzeichentreffer lassen sich weder Band noch Locator zuverlässig ableiten. Der Simplelogfile-Interpreter kann deshalb nur den globalen Worked-Status setzen. Er erzeugt keine bandbezogene `X`-Markierung, kein Worked-Großfeld und keine belastbare Grundlage für den Band-Upgrade-Hinweis nach einem Logeintrag.
|
||||
|
||||
Den Pfad der Logdatei und den regulären Ausdruck im Reiter **Log sync** eintragen. Für bandbezogene Auswertungen sollte nach Möglichkeit eine der Netzwerkschnittstellen verwendet werden.
|
||||
Den Pfad der Textdatei im Reiter **Log sync** auswählen. Fehlt die Datei, legt KST4Contest sie an und zeigt einmalig einen nicht blockierenden Hinweis mit dem Pfad und den nächsten Prüfschritten an. Lese- oder Erstellungsfehler werden protokolliert; die minütliche Auswertung läuft beim nächsten Termin weiter. Für bandbezogene Auswertungen sollte nach Möglichkeit eine der Netzwerkschnittstellen verwendet werden.
|
||||
|
||||
Der aus dem Simplelogfile abgeleitete Worked-Status wird nicht in der internen SQLite-Datenbank gespeichert. Die ausgewählte Datei ist die dauerhafte Quelle und wird auch nach einem Neustart erneut eingelesen. Der Interpreter setzt nur positive Worked-Markierungen; er entfernt während der laufenden Programmsitzung keine bereits gesetzte Markierung und führt beim Wechsel zu einem neuen Contest keinen automatischen Reset durch. Vor jedem Contest sollte deshalb geprüft werden, ob das Logprogramm genau die aktuelle Contestdatei beschreibt.
|
||||
|
||||
---
|
||||
|
||||
@@ -163,7 +165,7 @@ Neben der QSO-Synchronisation übertragen UCXLog und andere Programme auch die *
|
||||
|
||||

|
||||
|
||||
**Ergebnis**: Die eigene QRG muss im Chat nie mehr manuell eingegeben werden – ein Klick auf den MYQRG-Button oder die Verwendung der Variable im Beacon genügt.
|
||||
**Ergebnis**: Eine aktivierte Schnittstelle aktualisiert `MYQRG`, sobald sie tatsächlich gültige Frequenzpakete liefert. Das Aktivieren allein erzeugt noch keine QRG. Kommen keine passenden Pakete an, muss die Schnittstelle geprüft oder die QRG nach dem Deaktivieren beider automatischen Quellen manuell gepflegt werden.
|
||||
|
||||
**Quellen für die eigene QRG (MYQRG):**
|
||||
- UCXLog, N1MM+, DXLog.net, QARTest via UDP-Port 12060
|
||||
@@ -191,7 +193,7 @@ Für DM5M-typische Setups (2 Radios, 2 Computer, eine KST4Contest-Instanz oder z
|
||||
|
||||
## Interne Datenbank
|
||||
|
||||
KST4Contest speichert Worked-, NOT-QRV- und Großfeldinformationen in einer eigenen SQLite-Datenbank. Sie ist von der Datenbank des Logprogramms unabhängig.
|
||||
KST4Contest speichert Worked-, NOT-QRV- und Großfeldinformationen aus den Netzwerkschnittstellen sowie manuelle Markierungen in einer eigenen SQLite-Datenbank. Sie ist von der Datenbank des Logprogramms unabhängig. Simplelogfile-Treffer sind davon ausgenommen und werden bei jeder Programmsitzung aus der ausgewählten Datei neu abgeleitet.
|
||||
|
||||
Die Datenquellen liefern unterschiedlich genaue Informationen:
|
||||
|
||||
@@ -201,6 +203,6 @@ Die Datenquellen liefern unterschiedlich genaue Informationen:
|
||||
| QSO-UDP-Listener | ja | ja, wenn im Paket enthalten | ja, wenn Band und Locator enthalten sind |
|
||||
| Win-Test-Netzwerk-Listener | ja | ja | ja, wenn ein Locator enthalten ist |
|
||||
|
||||
Die Daten werden beim Programmstart wieder geladen und bei neuen Logeinträgen während des Betriebs aktualisiert. Sie laufen nach drei Tagen automatisch ab. Ein Reset vor jedem Contest ist daher normalerweise nicht erforderlich.
|
||||
Die in SQLite gespeicherten Daten werden beim Programmstart wieder geladen und bei neuen Logeinträgen während des Betriebs aktualisiert. Sie laufen nach drei Tagen automatisch ab. Ein Reset vor jedem Contest ist daher normalerweise nicht erforderlich. Für den Simplelogfile-Interpreter gilt diese Lebensdauer nicht: Seine Datei bleibt die dauerhafte Quelle und wird nicht automatisch auf einen neuen Contest zurückgesetzt.
|
||||
|
||||
Ein vollständiger manueller Reset entfernt Worked-Markierungen, NOT-QRV-Tags und Worked-Großfelder gemeinsam. Weitere Einzelheiten: [Worked Station Database Settings](de-Konfiguration#worked-station-database-settings-gearbeitete-stationen-datenbank).
|
||||
Ein vollständiger manueller Reset entfernt Worked-Markierungen, NOT-QRV-Tags und Worked-Großfelder gemeinsam. Weitere Einzelheiten: [Worked Station Database Settings](de-Konfiguration#worked-station-database-settings-gearbeitete-stationen-datenbank).
|
||||
|
||||
@@ -80,6 +80,10 @@ v1.42 brings several previously separate calculations together. Band information
|
||||
|
||||
- **DXLog full-log import:** In addition to `contactinfo`, the UCXLog-compatible UDP listener processes `contactreplace`. This allows a complete log broadcast by DXLog.net to be imported.
|
||||
|
||||
- **Defined Simplelogfile behaviour:** The selected text file is evaluated once per minute using a fixed callsign pattern. Matches set the global Worked status for all active variants of the base callsign but are not persisted in SQLite. A missing file is created, and read or creation errors do not terminate the periodic task.
|
||||
|
||||
- **Guarded automatic QRG updates:** `MYQRG` is updated only by an enabled interface which actually supplies valid `RadioInfo` or Win-Test `STATUS` packets. An enabled source which provides no data does not remove the need for a functional check or manual QRG maintenance.
|
||||
|
||||
- **Improved version comparison:** Versions are compared semantically so that patch releases and Nightly versions are not misclassified by conversion to a floating-point number.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -14,14 +14,15 @@ After the first start, the **settings window** opens – this is the central sta
|
||||
|
||||
### Login and Chat Categories
|
||||
|
||||
Enter your ON4KST chat credentials here (callsign and password).
|
||||
Also, select the **primary chat category** (e.g., IARU Region 1 VHF/Microwave).
|
||||
Enter the one local login callsign and password used for the ON4KST chat. Also select the **primary chat category** (e.g. IARU Region 1 VHF/Microwave).
|
||||
|
||||
With the option for a **second chat** (Multi-Channel Login), you can log in to another category simultaneously (e.g., UHF/SHF). Both chats will then be monitored in parallel. You can optionally specify a different login name for the second chat (useful for Opposite Station Multi-Callsign Logging).
|
||||
The **second chat** option (Multi-Channel Login) adds another category (e.g. UHF/SHF) to the same ON4KST TCP session through Single Sign-on. KST4Contest does not open a second TCP connection and does not use another local login callsign or password.
|
||||
|
||||
**Name in Chat 2** configures only the visible, category-specific name field for the second category. It is neither another login nor a message destination. The visible name field, message context, QRG and beacon remain separate for each category. **Opposite Station Multi-Callsign Login Tagging**, by contrast, refers exclusively to remote stations which appear in the chat under several complete visible callsign variants.
|
||||
|
||||
### Callsign and Locator
|
||||
|
||||
Enter your own callsign and Maidenhead locator (6 characters, e.g., `JN49IJ`). These values are needed for distance and direction calculations.
|
||||
Enter your own callsign and Maidenhead locator (6 characters, e.g. `JN49IJ`). The locator is not part of authentication. ON4KST shares it between both categories in the one TCP session, and KST4Contest also uses it for distance and direction calculations.
|
||||
|
||||
### Active Bands
|
||||
|
||||
@@ -127,13 +128,15 @@ The **Log sync** tab selects the sources from which KST4Contest imports worked s
|
||||
| **General QSO UDP listener** | QSO packets from UCXLog, QARTest, N1MM+ and DXLog.net | global and per-band Worked status plus worked grid square where both band and locator are transmitted |
|
||||
| **Win-Test network listener** | native Win-Test network packets | global and per-band Worked status, locator information and, depending on the settings, QRG synchronisation and sked handover |
|
||||
|
||||
The file-based interpreter is mainly useful when the logging application provides no supported network interface. A callsign match alone, however, contains neither a reliable band nor a locator. Use one of the network listeners wherever possible if per-band information is required.
|
||||
The file-based interpreter reads the selected text file once per minute using a fixed callsign pattern. Matches are normalised to base callsigns and set only the global Worked status for all active variants. It is mainly useful when the logging application provides no supported network interface. A callsign match alone contains neither a reliable band nor a locator, so use one of the network listeners wherever possible if per-band information is required.
|
||||
|
||||
If the selected file does not exist, KST4Contest creates it and displays its full path together with checks for initial setup and the next contest. The file itself is the durable source; Worked marks derived from it are not stored in SQLite and are not reset automatically for a new contest.
|
||||
|
||||
The general QSO UDP listener is the recommended interface for UCXLog, QARTest, N1MM+ and DXLog.net. QSO and `RadioInfo` packets use the same configurable UDP port; the default is `12060`. Separate options in **Log sync** and **TRX sync** determine whether the received QSO and frequency information is processed.
|
||||
|
||||
Win-Test uses its own network protocol and therefore has a separate listener. Its default port is `9871`. If this port is changed while the listener is enabled, KST4Contest restarts the Win-Test listener on the new port. After changing the shared UDP port `12060`, KST4Contest must instead be restarted completely.
|
||||
|
||||
All enabled input paths may be used in parallel. Their Worked information is merged into the same internal database; identical reports do not create separate Worked states. KST4Contest must be running when a QSO is saved unless the logging application can resend the existing log.
|
||||
All enabled input paths may be used in parallel and identical reports do not create separate Worked states. Network-derived Worked information is stored in the internal database. Simplelogfile marks remain runtime state derived from the selected file. KST4Contest must be running when a network QSO is transmitted unless the logging application can resend the existing log.
|
||||
|
||||
Configuration of the individual logging applications, band and locator handling, and the Win-Test sked handover are described under [Log Synchronisation](en-Log-Sync).
|
||||
|
||||
@@ -153,7 +156,7 @@ TRX synchronisation imports the current frequency from the logging application a
|
||||
| **Win-Test STATUS** | `Win-Test STATUS QRG Sync` | Processes the main or pass frequency from native Win-Test `STATUS` packets. The Win-Test listener uses its separately configured port, which defaults to `9871`. |
|
||||
| **Manual entry** | Disable both automatic QRG sources | The local QRG can be entered manually in the main window. |
|
||||
|
||||
The general listener is intended for logging applications which transmit compatible `RadioInfo` packets. Depending on their individual configuration, this includes UCXLog, N1MM+, QARTest and DXLog.net. QSO and `RadioInfo` packets use the same port configured under **Log sync**, but separate options determine whether KST4Contest processes QSO information, TRX information or both packet types.
|
||||
The general listener is intended for logging applications which transmit compatible `RadioInfo` packets. Depending on their individual configuration, this includes UCXLog, N1MM+, QARTest and DXLog.net. QSO and `RadioInfo` packets use the same port configured under **Log sync**, but separate options determine whether KST4Contest processes QSO information, TRX information or both packet types. An automatic QRG source must be enabled and actually supply valid packets; enabling it alone does not update `MYQRG`.
|
||||
|
||||
Restart KST4Contest after changing the shared UDP port. Changes to the two QRG-sync checkboxes take effect immediately.
|
||||
|
||||
@@ -163,7 +166,7 @@ Both automatic sources update `MYQRG` only. This is the local QRG of the first o
|
||||
|
||||
If a second chat is enabled, its QRG remains independent. It is not derived from incoming TRX packets and is available through `SECONDQRG`. The first category can therefore follow the logging application's frequency automatically while a separate QRG is entered manually for the second category.
|
||||
|
||||
As soon as at least one automatic QRG source is enabled, the first category's QRG field in the main window is bound to the received value. Manual entry becomes available again when both the general RadioInfo listener and Win-Test STATUS synchronisation are disabled.
|
||||
When at least one automatic QRG source is enabled and supplies a valid value, the first category's QRG field in the main window follows the received frequency. If the expected packets do not arrive, verify the interface before the contest. Manual entry is available when both the general RadioInfo listener and Win-Test STATUS synchronisation are disabled.
|
||||
|
||||
### Main or Pass Frequency from Win-Test
|
||||
|
||||
@@ -576,7 +579,7 @@ KST4Contest uses one shared timer for both chat categories. Each category nevert
|
||||
- **Beacon message** contains the public message for that category.
|
||||
- **Shared beacon interval** sets the common interval used by both categories.
|
||||
|
||||
When both beacons are enabled, they are sent one after the other in their respective categories during the same timer run. The second beacon is only considered while the second chat is enabled and connected.
|
||||
When both beacons are enabled, they are sent one after the other in their respective categories during the same timer run. The second beacon is only considered while the second category is enabled and fully synchronised in the same ON4KST session.
|
||||
|
||||
### Interval and timer behaviour
|
||||
|
||||
@@ -916,6 +919,8 @@ The internal SQLite database stores contest-related state independently of the l
|
||||
- manually assigned NOT-QRV marks per band, and
|
||||
- worked four-character grid squares per band.
|
||||
|
||||
Worked marks from the Simplelogfile interpreter are the exception. They are copied from the selected file into runtime state only and are not persisted in SQLite. The file is the durable source and has no automatic contest reset.
|
||||
|
||||
The normalised callsign, without visible chat brackets or category formatting, is used as the key. This allows active variants of the same callsign to be evaluated consistently.
|
||||
|
||||
Worked and NOT-QRV information expires automatically three days after its most recent change. Stored grid squares expire three days after the corresponding log entry. A manual reset before every contest is therefore normally unnecessary.
|
||||
|
||||
@@ -67,6 +67,8 @@ Enable only interfaces which are actually required and have already been tested:
|
||||
|
||||
A contest is not an ideal time to investigate radio conditions and a newly enabled network interface at the same time.
|
||||
|
||||
Before each contest, verify that the logging application writes the current contest file to the path selected for the Simplelogfile. A test QSO should appear as globally worked within one minute. KST4Contest does not reset Worked marks derived from this file automatically when the contest changes.
|
||||
|
||||
---
|
||||
|
||||
## Basic Contest Cycle
|
||||
@@ -89,7 +91,7 @@ KST4Contest keeps the required information together between these steps. Changin
|
||||
|
||||
## CQ Operation
|
||||
|
||||
During operation on a mainly fixed CQ frequency, `MYQRG` and `SECONDQRG` should match the frequencies actually in use. Enabled TRX synchronisation can update `MYQRG` automatically. Without an automatic source, the value must be maintained manually.
|
||||
During operation on a mainly fixed CQ frequency, `MYQRG` and `SECONDQRG` should match the frequencies actually in use. TRX synchronisation can update `MYQRG` automatically only when it is enabled and actually supplies valid packets. Before the contest, verify this with a real frequency change. Without a working automatic source, the value must be maintained manually.
|
||||
|
||||
The beacon can publish the current QRG, locator and antenna direction in the chat at regular intervals. Its variables are evaluated again for every transmission.
|
||||
|
||||
@@ -257,7 +259,7 @@ Another particularly consistent operating method uses the station map as a geogr
|
||||
|
||||
The map consequently becomes a spatial list of the stations still to be worked. This workflow is optional. It requires reliable log synchronisation and a working DX Cluster connection and is mainly useful for operators who deliberately want to organise the contest in this geographical way.
|
||||
|
||||
[Watch G1YBB demonstrate this workflow.](https://www.youtube.com/watch?v=BCNCjowPgec)
|
||||
[Watch G1YBB demonstrate this workflow.](https://www.youtube.com/watch?v=lMQZMiSHlUI)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ This distinction matters. A callsign already worked on one band may still be use
|
||||
|
||||
[Log Synchronisation](en-Log-Sync) imports new QSOs from the logging application. The amount of information available depends on the interface being used:
|
||||
|
||||
- The file-based Simplelogfile interpreter detects callsigns only. It can therefore set only the global Worked status.
|
||||
- The file-based Simplelogfile interpreter reads the selected text file once per minute using a fixed callsign pattern. Matches are normalised to the base callsign and set only the global Worked status for all active variants.
|
||||
- The QSO UDP interfaces and the Win-Test network listener can also provide the band.
|
||||
- If the log packet contains a valid locator, KST4Contest additionally stores the worked four-character grid square for that band.
|
||||
|
||||
@@ -206,7 +206,9 @@ In plain terms: an automatically detected hint means "probably active on this ba
|
||||
|
||||
### Storage and lifetime
|
||||
|
||||
Worked, NOT-QRV and worked-grid information is stored in the internal SQLite database and restored on the next start. Entries expire automatically after three days, so a reset before every contest is normally unnecessary.
|
||||
Worked, NOT-QRV and worked-grid information received from network interfaces, together with manual marks, is stored in the internal SQLite database and restored on the next start. Entries expire automatically after three days, so a reset before every contest is normally unnecessary.
|
||||
|
||||
Simplelogfile matches are excluded. They are not persisted in SQLite but are derived from the selected file during each application session. The file is therefore the durable source. The interpreter does not remove an existing Worked mark during the current session and does not reset the state automatically for a new contest.
|
||||
|
||||
A manual reset under **Workedstn database** removes all Worked marks, NOT-QRV marks and stored worked grid squares. The known callsign rows remain in the database. See [Worked Station Database Settings](en-Configuration#worked-station-database-settings) for details.
|
||||
|
||||
@@ -307,7 +309,7 @@ Configuration, recognised QRG requests and category handling: [Configuration –
|
||||
|
||||
## Multi-Channel Login (from v1.26)
|
||||
|
||||
Simultaneous login to **two chat categories** (e.g. 144 MHz and 432 MHz). Both chats are monitored in parallel.
|
||||
KST4Contest signs in to ON4KST once with one local login callsign and password. One locator is shared by the entire TCP session. A second chat category (e.g. 144 MHz and 432 MHz) is added through Single Sign-on within the same session. Both categories are monitored in parallel, while the visible category-specific name field, message context, QRG and beacon remain separate for each category.
|
||||
|
||||
---
|
||||
|
||||
@@ -321,7 +323,7 @@ The green private-message age scale remains available in both designs. Text colo
|
||||
|
||||
## Opposite Station Multi-Callsign Login Tagging (from v1.26)
|
||||
|
||||
Support for stations that are active in the chat with multiple callsigns simultaneously (e.g. expedition setups).
|
||||
Support for remote stations which are active in the chat under several complete visible callsign variants at the same time (e.g. expedition setups). The complete callsign and chat category remain separate participant identities, while Worked, band and priority data are shared through the base callsign.
|
||||
|
||||
---
|
||||
|
||||
@@ -437,7 +439,7 @@ Sniffed: (DN9APW-70 > 9A0BB-23) pse sked 19:30
|
||||
|
||||
This distinction is intentional. Monitoring aggregates the variants so that their communication remains visible. Message routing does not aggregate them because the intended login would otherwise become ambiguous.
|
||||
|
||||
The base callsign is evaluated across both connected chat categories. The category of each individual message remains unchanged.
|
||||
The base callsign is evaluated across both chat categories in the shared ON4KST session. The category of each individual message remains unchanged.
|
||||
|
||||
The monitoring view includes:
|
||||
|
||||
@@ -602,7 +604,7 @@ The score is calculated for the normalised base callsign. A sked entered for an
|
||||
|
||||
### How are multiple suffixes and chat categories handled?
|
||||
|
||||
Active callsigns such as `9A0BB-2`, `9A0BB-70`, `9A0BB-23` and `9A0BB-13` remain separate chat members. Messages can therefore still be addressed to the complete callsign in the correct chat category.
|
||||
Active complete visible callsign variants such as `9A0BB-2`, `9A0BB-70`, `9A0BB-23` and `9A0BB-13` remain separate chat members. Messages can therefore still be addressed to the complete callsign in the correct chat category.
|
||||
|
||||
Worked, band, NOT-QRV and score information belongs to the common base callsign `9A0BB`. The score is calculated once and projected to all active variants. The user list may consequently contain several separate rows with the same score, while the priority list contains only one entry for the base station.
|
||||
|
||||
@@ -703,7 +705,7 @@ The timeline is a preview. AirScout data can change, and a stored sked guarantee
|
||||
|
||||
KST4Contest can send recurring CQ messages to the public chat. The beacon is intended for longer periods of calling CQ on a fixed frequency: it publishes the local QRG regularly without requiring the operator to enter the same text again.
|
||||
|
||||
Both chat categories use one shared interval, but each category has its own enable setting and message template. The second beacon is only sent while the second chat is enabled and connected.
|
||||
Both chat categories use one shared interval, but each category has its own enable setting and message template. The second beacon is only sent while the second category is enabled and fully synchronised in the shared ON4KST session.
|
||||
|
||||
Global variables such as `MYQRG`, `SECONDQRG`, `MYLOCATOR` and `MYQTF` are resolved immediately before every transmission. A QRG updated by the logging software can therefore appear in the next beacon.
|
||||
|
||||
@@ -718,7 +720,7 @@ Configuration, timer behaviour and available variables: [Configuration – Beaco
|
||||
|
||||
## Simplelogfile
|
||||
|
||||
File-based log evaluation using regex. Details: [Log Synchronisation](en-Log-Sync#method-1-universal-file-based-callsign-interpreter-simplelogfile).
|
||||
The Simplelogfile interpreter reads callsigns from a selected text file once per minute. If the file is missing, KST4Contest creates it and displays a non-blocking notice with its path and the initial setup and contest checks. Details: [Log Synchronisation](en-Log-Sync#method-1-universal-file-based-callsign-interpreter-simplelogfile).
|
||||
---
|
||||
|
||||
## Global Message Views
|
||||
|
||||
@@ -10,17 +10,19 @@ KST4Contest imports worked stations from the logging application and derives the
|
||||
|
||||
## Method 1: Universal File Based Callsign Interpreter (Simplelogfile)
|
||||
|
||||
KST4Contest reads a log file and searches it for callsigns using a configurable regular expression. The file is read only and is never modified. Binary log files can also be used; content which cannot be interpreted as text is skipped.
|
||||
KST4Contest reads the selected text file once per minute and searches it for callsigns using a fixed built-in regular expression. Each match is normalised to its base callsign, so the global Worked status applies to every currently active chat variant of that callsign.
|
||||
|
||||
The advantage is broad compatibility: no dedicated network interface is required from the logging application.
|
||||
|
||||
The limitation is equally clear. A callsign match alone provides neither a reliable band nor a locator. The Simplelogfile interpreter can therefore set only the global Worked status. It does not create a per-band `X`, a worked-grid record or a reliable basis for the band-upgrade hint after a log entry.
|
||||
|
||||
Configure the log-file path and regular expression in the **Log sync** tab. Use one of the network interfaces where possible if band-specific information is required.
|
||||
Select the text-file path in the **Log sync** tab. If the file is missing, KST4Contest creates it and displays a one-time, non-blocking notice with the path and the checks to perform next. Read or creation errors are logged; the scheduled task continues with its next one-minute pass. Use one of the network interfaces where possible if band-specific information is required.
|
||||
|
||||
Worked status derived from the Simplelogfile is not stored in the internal SQLite database. The selected file is the durable source and is read again after every restart. The interpreter only adds positive Worked marks; it does not remove an existing mark during the current application session and does not reset automatically for a new contest. Before each contest, verify that the logging application is writing the current contest log to this exact file.
|
||||
|
||||
---
|
||||
|
||||
# Method 2: Network Listener for QSO UDP Packets – Recommended
|
||||
## Method 2: Network Listener for QSO UDP Packets – Recommended
|
||||
|
||||
UCXLog, QARTest, N1MM+ and DXLog.net can transmit a UDP packet when a QSO is saved. KST4Contest receives these packets on port `12060` by default and imports the callsign together with any band and locator information they contain.
|
||||
|
||||
@@ -164,7 +166,7 @@ In addition to QSO synchronisation, UCXLog and other programs also transmit the
|
||||
|
||||

|
||||
|
||||
**Result**: Your own QRG never needs to be typed manually in the chat – clicking the MYQRG button or using the variable in the beacon is sufficient.
|
||||
**Result**: An enabled interface updates `MYQRG` when it actually supplies valid frequency packets. Enabling an interface does not create a QRG on its own. If no suitable packets arrive, check the interface or disable both automatic sources and maintain the QRG manually.
|
||||
|
||||
**Sources for your own QRG (MYQRG):**
|
||||
- UCXLog, N1MM+, DXLog.net, QARTest via UDP port 12060
|
||||
@@ -192,7 +194,7 @@ For DM5M-style setups (2 radios, 2 computers, one KST4Contest instance or two se
|
||||
|
||||
## Internal Database
|
||||
|
||||
KST4Contest stores Worked, NOT-QRV and worked-grid information in its own SQLite database. This database is independent of the logging application's database.
|
||||
KST4Contest stores Worked, NOT-QRV and worked-grid information received from network interfaces, together with manual marks, in its own SQLite database. This database is independent of the logging application's database. Simplelogfile matches are excluded and are derived again from the selected file in each application session.
|
||||
|
||||
The input sources provide different levels of detail:
|
||||
|
||||
@@ -202,6 +204,6 @@ The input sources provide different levels of detail:
|
||||
| QSO UDP listener | yes | yes, if included in the packet | yes, if both band and locator are available |
|
||||
| Win-Test network listener | yes | yes | yes, if a locator is available |
|
||||
|
||||
The information is restored when KST4Contest starts and updated during operation when new log entries arrive. It expires automatically after three days, so a reset before every contest is normally unnecessary.
|
||||
The information stored in SQLite is restored when KST4Contest starts and updated during operation when new log entries arrive. It expires automatically after three days, so a reset before every contest is normally unnecessary. This lifetime does not apply to the Simplelogfile interpreter: its file remains the durable source and is not reset automatically for a new contest.
|
||||
|
||||
A complete manual reset removes Worked marks, NOT-QRV marks and worked grid squares together. See [Worked Station Database Settings](en-Configuration#worked-station-database-settings) for details.
|
||||
A complete manual reset removes Worked marks, NOT-QRV marks and worked grid squares together. See [Worked Station Database Settings](en-Configuration#worked-station-database-settings) for details.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Connecting to the Chat
|
||||
|
||||
Before connecting for the first time, configure at least the callsign, password, locator and primary chat category in the settings window. If a second category is required, its login must also be enabled and configured completely.
|
||||
Before connecting for the first time, configure the local login callsign and password, the locator shared by the TCP session, and the primary chat category in the settings window. If a second category is required, enable and select it there. KST4Contest signs in once and adds the second category through Single Sign-on within the same TCP session.
|
||||
|
||||
The connection can be started in two ways:
|
||||
|
||||
@@ -54,7 +54,7 @@ Both indicators flash for approximately twelve seconds and then disappear. Their
|
||||
|
||||
### PM Window (top left)
|
||||
|
||||
The PM window shows private messages addressed to the local chat logins and the corresponding outgoing replies.
|
||||
The PM window shows private messages addressed to the local login callsign and the corresponding outgoing replies.
|
||||
|
||||
Messages not sent by the local station are also shown when their text contains the configured local login callsign, ignoring letter case. This applies to public messages addressed to `ALL` and to directed messages between other chat participants. The informal description **“gossip detection”** is particularly apt for the latter case.
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package kst4contest.controller;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.SQLException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
@@ -2016,6 +2017,115 @@ private ObservableList<String>
|
||||
return matchingMembers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the global Worked state from the Simplelogfile to every active
|
||||
* callsign variant with the same base callsign. UI-backed state is changed only
|
||||
* on the JavaFX Application Thread.
|
||||
*
|
||||
* @param workedBaseCalls normalized callsigns detected in the selected file
|
||||
*/
|
||||
public void applySimpleLogWorkedBaseCalls(Set<String> workedBaseCalls) {
|
||||
Set<String> normalizedWorkedBaseCalls = normalizeWorkedBaseCalls(workedBaseCalls);
|
||||
if (normalizedWorkedBaseCalls.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
runOnFxThread(() -> {
|
||||
int changedMembers = markSimpleLogWorkedMembers(
|
||||
activeChatMembersByCallAndCategory.values(), normalizedWorkedBaseCalls);
|
||||
int changedClusterMessages = markSimpleLogWorkedClusterMessages(
|
||||
lst_clusterMemberList, normalizedWorkedBaseCalls);
|
||||
|
||||
if (changedMembers > 0) {
|
||||
fireUserListUpdate("Simplelogfile Worked status updated");
|
||||
}
|
||||
|
||||
LOGGER.log(Level.FINE,
|
||||
"Simplelogfile marked {0} active members and {1} cluster messages as worked.",
|
||||
new Object[] { changedMembers, changedClusterMessages });
|
||||
});
|
||||
}
|
||||
|
||||
static int markSimpleLogWorkedMembers(
|
||||
Collection<ChatMember> members,
|
||||
Set<String> workedBaseCalls
|
||||
) {
|
||||
if (members == null || workedBaseCalls == null || workedBaseCalls.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int changed = 0;
|
||||
for (ChatMember member : members) {
|
||||
if (member == null || member.isWorked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String memberCall = member.getCallSignRaw() != null
|
||||
? member.getCallSignRaw() : member.getCallSign();
|
||||
String baseCall = ChatMember.normalizeCallSignToBaseCallSign(memberCall);
|
||||
if (baseCall != null && workedBaseCalls.contains(baseCall.toUpperCase(Locale.ROOT))) {
|
||||
member.setWorked(true);
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int markSimpleLogWorkedClusterMessages(
|
||||
Collection<ClusterMessage> clusterMessages,
|
||||
Set<String> workedBaseCalls
|
||||
) {
|
||||
if (clusterMessages == null || workedBaseCalls == null || workedBaseCalls.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int changed = 0;
|
||||
for (ClusterMessage clusterMessage : clusterMessages) {
|
||||
if (clusterMessage == null || clusterMessage.isReceiverWkd()
|
||||
|| clusterMessage.getReceiver() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ChatMember receiver = clusterMessage.getReceiver();
|
||||
String receiverCall = receiver.getCallSignRaw() != null
|
||||
? receiver.getCallSignRaw() : receiver.getCallSign();
|
||||
String baseCall = ChatMember.normalizeCallSignToBaseCallSign(receiverCall);
|
||||
if (baseCall != null && workedBaseCalls.contains(baseCall.toUpperCase(Locale.ROOT))) {
|
||||
clusterMessage.setReceiverWkd(true);
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static Set<String> normalizeWorkedBaseCalls(Set<String> workedBaseCalls) {
|
||||
if (workedBaseCalls == null || workedBaseCalls.isEmpty()) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
Set<String> normalizedCalls = new HashSet<>();
|
||||
for (String callSign : workedBaseCalls) {
|
||||
String baseCall = ChatMember.normalizeCallSignToBaseCallSign(callSign);
|
||||
if (baseCall != null && !baseCall.isBlank()) {
|
||||
normalizedCalls.add(baseCall.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
return Set.copyOf(normalizedCalls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the UI after a missing Simplelogfile was created successfully.
|
||||
*
|
||||
* @param filePath absolute path of the new file
|
||||
*/
|
||||
public void notifySimpleLogFileCreated(Path filePath) {
|
||||
if (filePath == null || statusListener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runOnFxThread(() -> statusListener.onSimpleLogFileCreated(filePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the band-specific NOT-QRV state to every active category variant of the
|
||||
* same base callsign. The database already uses callSignRaw as its key; applying
|
||||
@@ -4098,4 +4208,4 @@ private ObservableList<String>
|
||||
|
||||
return "Sniffed: (" + senderCall + " > " + receiverCall + ") " + msgText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import kst4contest.model.ThreadStateMessage;
|
||||
|
||||
public interface StatusUpdateListener {
|
||||
@@ -33,4 +35,13 @@ public interface StatusUpdateListener {
|
||||
// Optional for non-UI listeners.
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Called after KST4Contest successfully creates a missing Simplelogfile.
|
||||
*
|
||||
* @param filePath absolute path of the newly created file
|
||||
*/
|
||||
default void onSimpleLogFileCreated(Path filePath) {
|
||||
// Optional for non-UI listeners.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -14,73 +15,50 @@ import kst4contest.model.ChatMember;
|
||||
|
||||
public class UCXLogFileToHashsetParser {
|
||||
|
||||
public BufferedReader fileReader;
|
||||
// private final String PTRN_CallSign = "(([a-zA-Z]{1,2}[\\d{1}]?\\/)?(\\d{1}[a-zA-Z][\\d{1}][a-zA-Z]{1,3})((\\/p)|(\\/\\d))?)|(([a-zA-Z0-9]{1,2}[\\d{1}]?\\/)?(([a-zA-Z]{1,2}(\\d{1}[a-zA-Z]{1,4})))((\\/p)|(\\/\\d))?)"; //OLD, S51AR for example will not work
|
||||
private final String PTRN_CallSign = "(([a-zA-Z]{1,2}[\\d]{1}?\\/)?(\\d{1}[a-zA-Z][\\d]{1}[a-zA-Z]{1,3})((\\/p)|(\\/\\d))?)|(([a-zA-Z0-9]{1,2}[\\d]{1}?\\/)?(([a-zA-Z]{1,2}(\\d{1}[a-zA-Z]{1,4})))((\\/p)|(\\/\\d))?)|([A-Z]\\d{2}[A-Z]{1,3})";
|
||||
|
||||
private static final Pattern CALL_SIGN_PATTERN = Pattern.compile(
|
||||
"(([a-zA-Z]{1,2}[\\d]{1}?\\/)?(\\d{1}[a-zA-Z][\\d]{1}[a-zA-Z]{1,3})((\\/p)|(\\/\\d))?)"
|
||||
+ "|(([a-zA-Z0-9]{1,2}[\\d]{1}?\\/)?(([a-zA-Z]{1,2}(\\d{1}[a-zA-Z]{1,4})))((\\/p)|(\\/\\d))?)"
|
||||
+ "|([A-Z]\\d{2}[A-Z]{1,3})");
|
||||
|
||||
private final Path logFile;
|
||||
|
||||
public UCXLogFileToHashsetParser(String filePathAndName) {
|
||||
|
||||
try {
|
||||
fileReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePathAndName))));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.logFile = Path.of(filePathAndName);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a line of the ucxlog-Logfile inhibits a Callsign<br/>
|
||||
* <b>returns ChatMember = null, if no frequency found</b>
|
||||
*
|
||||
* @param chatMessage
|
||||
*/
|
||||
private ChatMember checkIfLineInhibitsCallSign(String line) {
|
||||
|
||||
Pattern pattern = Pattern.compile(PTRN_CallSign);
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
|
||||
String matchedString = "";
|
||||
private String findLastCallSign(String line) {
|
||||
Matcher matcher = CALL_SIGN_PATTERN.matcher(line);
|
||||
String matchedCallSign = "";
|
||||
|
||||
while (matcher.find()) {
|
||||
|
||||
matchedString = matcher.group();
|
||||
// System.out.println("[UCXLogFile:] Processed worked Callsign from file: " + matchedString);
|
||||
|
||||
matchedCallSign = matcher.group();
|
||||
}
|
||||
|
||||
ChatMember newChatMember = new ChatMember();
|
||||
|
||||
newChatMember.setCallSign(matchedString.toUpperCase());
|
||||
|
||||
return newChatMember;
|
||||
|
||||
return matchedCallSign.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an ucxlog-live-file (full qualified path given by constructor
|
||||
* argument), looks by regex for callsigns and builds a hashmap with only one
|
||||
* entry by callsign
|
||||
* Parses the selected log file and returns every detected station as a
|
||||
* normalized base callsign. The reader is closed after each pass so the logging
|
||||
* application can continue replacing or rotating the file.
|
||||
*
|
||||
* @return unique normalized base callsigns found in the file
|
||||
* @throws IOException if the file cannot be read
|
||||
*/
|
||||
public HashMap<String, String> parse() throws IOException {
|
||||
public Set<String> parse() throws IOException {
|
||||
Set<String> workedBaseCalls = new HashSet<>();
|
||||
|
||||
HashMap<String, String> chatMemberMap = new HashMap();
|
||||
|
||||
String line;
|
||||
while ((line = fileReader.readLine()) != null) {
|
||||
// System.out.println("raw: " + line);
|
||||
ChatMember temp = checkIfLineInhibitsCallSign(line);
|
||||
|
||||
if (temp.getCallSign() != "") {
|
||||
chatMemberMap.put(temp.getCallSign(), temp.getCallSign());
|
||||
try (BufferedReader fileReader = Files.newBufferedReader(logFile, Charset.defaultCharset())) {
|
||||
String line;
|
||||
while ((line = fileReader.readLine()) != null) {
|
||||
String matchedCallSign = findLastCallSign(line);
|
||||
String baseCallSign = ChatMember.normalizeCallSignToBaseCallSign(matchedCallSign);
|
||||
if (baseCallSign != null && !baseCallSign.isBlank()) {
|
||||
workedBaseCalls.add(baseCallSign.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
}
|
||||
// System.out.println(chatMemberMap.size());
|
||||
return chatMemberMap;
|
||||
|
||||
return workedBaseCalls;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,325 +1,100 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import kst4contest.model.ChatMember;
|
||||
import kst4contest.model.ClusterMessage;
|
||||
import kst4contest.view.GuiUtils;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class UserActualizationTask extends TimerTask {
|
||||
|
||||
private ChatController client;
|
||||
private static final Logger LOGGER = Logger.getLogger(UserActualizationTask.class.getName());
|
||||
|
||||
private final ChatController client;
|
||||
|
||||
public UserActualizationTask(ChatController client) {
|
||||
|
||||
this.client = client;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
Thread.currentThread().setName("UserActualizationTask");
|
||||
|
||||
/*
|
||||
* File-based log synchronization is optional. Do not create, open or parse the
|
||||
* configured file while the feature is disabled.
|
||||
*/
|
||||
try {
|
||||
updateWorkedCallSignsFromFile();
|
||||
} catch (RuntimeException exception) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Unexpected failure while updating Worked callsigns from the Simplelogfile; "
|
||||
+ "the periodic task will continue.",
|
||||
exception);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWorkedCallSignsFromFile() {
|
||||
if (!client.getChatPreferences().isLogsynch_fileBasedWkdCallInterpreterEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// System.out.println("[Useract: ] Thread runned now");
|
||||
|
||||
// System.out.println("***********************Useract started");
|
||||
|
||||
/**
|
||||
* ******************************************since here: old mechanic for
|
||||
* marking worked stations by .ucx-file
|
||||
*/
|
||||
|
||||
HashMap<String, String> fetchedWorkedSet = new HashMap<>();
|
||||
// HashMap<String, String> fetchedWorkedSetUdpBckup = new HashMap<>();
|
||||
|
||||
File f = new File(this.client.getChatPreferences().getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly());
|
||||
if (!f.exists() && !f.isDirectory()) {
|
||||
try {
|
||||
f.createNewFile();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
String configuredFileName = client.getChatPreferences()
|
||||
.getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly();
|
||||
if (configuredFileName == null || configuredFileName.isBlank()) {
|
||||
LOGGER.warning("Cannot read the Simplelogfile because no file is selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
UCXLogFileToHashsetParser getWorkedCallsignsOfUCXLogFile = new UCXLogFileToHashsetParser(
|
||||
this.client.getChatPreferences().getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly());
|
||||
final Path logFile;
|
||||
try {
|
||||
logFile = Path.of(configuredFileName).toAbsolutePath().normalize();
|
||||
} catch (InvalidPathException exception) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Cannot use the configured Simplelogfile path: " + configuredFileName,
|
||||
exception);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean created = createMissingLogFile(logFile);
|
||||
if (!Files.isRegularFile(logFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
fetchedWorkedSet = getWorkedCallsignsOfUCXLogFile.parse();
|
||||
|
||||
System.out.println("USERACT: fetchedWorkedSet size: " + fetchedWorkedSet.size());
|
||||
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
Set<String> workedBaseCalls = new UCXLogFileToHashsetParser(logFile.toString()).parse();
|
||||
client.applySimpleLogWorkedBaseCalls(workedBaseCalls);
|
||||
LOGGER.log(Level.FINE,
|
||||
"Read {0} unique base callsigns from Simplelogfile {1}.",
|
||||
new Object[] { workedBaseCalls.size(), logFile });
|
||||
} catch (IOException exception) {
|
||||
LOGGER.log(Level.WARNING, "Cannot read Simplelogfile " + logFile, exception);
|
||||
}
|
||||
|
||||
ObservableList<ChatMember> praktiKSTActiveUserList = this.client.getLst_chatMemberList();
|
||||
|
||||
for (Iterator iterator = praktiKSTActiveUserList.iterator(); iterator.hasNext();) {
|
||||
ChatMember chatMember = (ChatMember) iterator.next();
|
||||
|
||||
// System.out.println(chatMember.getCallSign());
|
||||
// System.out.println("USERACT active user list entries " + praktiKSTActiveUserList.size());
|
||||
|
||||
if (fetchedWorkedSet.containsKey(chatMember.getCallSign())) {
|
||||
chatMember.setWorked(true);
|
||||
System.out.println("[USERACT, info:] marking Chatuser " + chatMember.getCallSign()
|
||||
+ " as worked, based on READONLY-Logfile.");
|
||||
}
|
||||
|
||||
// if (fetchedWorkedSetUdpBckup.containsKey(chatMember.getCallSign())) {
|
||||
// chatMember.setWorked(true);
|
||||
// System.out.println("[USERACT, info:] marking Chatuser " + chatMember.getCallSign() + " as worked, based on UDPLsnBackup-Logfile.");
|
||||
// }
|
||||
// GuiUtils.triggerGUIFilteredChatMemberListChange(this.client); //todo: quick and dirty gui fix
|
||||
if (created) {
|
||||
client.notifySimpleLogFileCreated(logFile);
|
||||
}
|
||||
|
||||
ObservableList<ClusterMessage> praktiKSTClusterList = this.client.getLst_clusterMemberList();
|
||||
|
||||
for (Iterator iterator = praktiKSTClusterList.iterator(); iterator.hasNext();) {
|
||||
ClusterMessage clusterMessage = (ClusterMessage) iterator.next();
|
||||
|
||||
if (fetchedWorkedSet.containsKey(clusterMessage.getReceiver().getCallSign())) {
|
||||
clusterMessage.setReceiverWkd(true);
|
||||
System.out.println("[USERACT, info:] marking Clusterspotted "
|
||||
+ clusterMessage.getReceiver().getCallSign() + " as worked.");
|
||||
}
|
||||
|
||||
// if (fetchedWorkedSetUdpBckup.containsKey(clusterMessage.getReceiver().getCallSign())) {
|
||||
// clusterMessage.setReceiverWkd(true);
|
||||
// System.out.println("[USERACT, info:] marking Clusterspotted "
|
||||
// + clusterMessage.getReceiver().getCallSign() + " as worked.");
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* ******************************************end here: old mechanic for marking
|
||||
* worked stations by .ucx-file
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* ******************************************since here: new mechanic for
|
||||
* marking worked stations by udp/adif based information
|
||||
*/
|
||||
// HashMap<String, String> fetchedWorkedMap = new HashMap<>();
|
||||
//
|
||||
// fetchedWorkedMap = this.client.getMap_ucxLogInfoWorkedCalls();
|
||||
|
||||
// ObservableList<ChatMember> praktiKSTActiveUserList1 = this.client.getLst_chatMemberList();
|
||||
//
|
||||
// for (Iterator iterator = praktiKSTActiveUserList.iterator(); iterator.hasNext();) {
|
||||
// ChatMember chatMember = (ChatMember) iterator.next();
|
||||
//
|
||||
// if (fetchedWorkedMap.containsKey(chatMember.getCallSign())) {
|
||||
// chatMember.setWorked(true);
|
||||
// System.out.println("[USERACT, info:] marking Chatuser " + chatMember.getCallSign() + " as worked based on UDP Log Info Collector.");
|
||||
// }
|
||||
// }
|
||||
|
||||
// ObservableList<ClusterMessage> praktiKSTClusterList1 = this.client.getLst_clusterMemberList();
|
||||
//
|
||||
// for (Iterator iterator = praktiKSTClusterList.iterator(); iterator.hasNext();) {
|
||||
// ClusterMessage clusterMessage = (ClusterMessage) iterator.next();
|
||||
//
|
||||
// if (fetchedWorkedMap.containsKey(clusterMessage.getReceiver().getCallSign())) {
|
||||
// clusterMessage.setReceiverWkd(true);
|
||||
// System.out.println("[USERACT, info:] marking Clusterspotted "
|
||||
// + clusterMessage.getReceiver().getCallSign() + " as worked based on UDP Log Info Collector.");
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
//
|
||||
// UCXLogFileToHashsetParser getWorkedCallsignsOfUCXLogFile = new UCXLogFileToHashsetParser(
|
||||
// "C:\\UcxLog\\Logs\\DO5AMF\\DVU322_I.UCX");
|
||||
// try {
|
||||
// fetchedWorkedSet = getWorkedCallsignsOfUCXLogFile.parse();
|
||||
// } catch (IOException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
//
|
||||
// ObservableList<ChatMember> praktiKSTActiveUserList = this.client.getLst_chatMemberList();
|
||||
//
|
||||
// for (Iterator iterator = praktiKSTActiveUserList.iterator(); iterator.hasNext();) {
|
||||
// ChatMember chatMember = (ChatMember) iterator.next();
|
||||
//
|
||||
// if (fetchedWorkedSet.containsKey(chatMember.getCallSign())) {
|
||||
// chatMember.setWorked(true);
|
||||
// System.out.println("[USERACT, info:] marking Chatuser " + chatMember.getCallSign() + " as worked.");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// ObservableList<ClusterMessage> praktiKSTClusterList = this.client.getLst_clusterMemberList();
|
||||
//
|
||||
// for (Iterator iterator = praktiKSTClusterList.iterator(); iterator.hasNext();) {
|
||||
// ClusterMessage clusterMessage = (ClusterMessage) iterator.next();
|
||||
//
|
||||
// if (fetchedWorkedSet.containsKey(clusterMessage.getReceiver().getCallSign())) {
|
||||
// clusterMessage.setReceiverWkd(true);
|
||||
// System.out.println("[USERACT, info:] marking Clusterspotted "
|
||||
// + clusterMessage.getReceiver().getCallSign() + " as worked.");
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
/**
|
||||
* ******************************************end here: new mechanic for marking
|
||||
* worked stations by udp/adif based information
|
||||
*
|
||||
*/
|
||||
|
||||
// System.out.println("[UserActualizationtask:] Userlist actualization will be performed now. "
|
||||
// + LocalDateTime.ofInstant(Instant.ofEpochMilli(scheduledExecutionTime()),
|
||||
// ZoneId.systemDefault()));
|
||||
|
||||
// ChatMessage actualizeUserMsg = new ChatMessage();
|
||||
// actualizeUserMsg.setDirectedToServer(true);
|
||||
// actualizeUserMsg.setMessage("/show users");
|
||||
|
||||
// client.getMessageTXBus().add(actualizeUserMsg);
|
||||
|
||||
// Enumeration<String> e = this.client.getChatMemberTable().keys();
|
||||
//
|
||||
// while (e.hasMoreElements()) {
|
||||
// String key = e.nextElement();
|
||||
//
|
||||
// System.out.println(this.client.getChatMemberTable().get(key).getCallSign() + ", "
|
||||
// + this.client.getChatMemberTable().get(key).getQra() + ": "
|
||||
// + this.client.getChatMemberTable().get(key).getFrequency());
|
||||
//
|
||||
// }
|
||||
|
||||
// System.out.println("[UserAct]: Show the Cluster with known frequencies now: ");
|
||||
//
|
||||
// Enumeration<String> e2 = this.client.getdXClusterMemberTable().keys();
|
||||
//
|
||||
// while (e2.hasMoreElements()) {
|
||||
// String key = e2.nextElement();
|
||||
//
|
||||
// System.out.println(this.client.getdXClusterMemberTable().get(key).getCallSign() + ", "
|
||||
// + this.client.getdXClusterMemberTable().get(key).getQra() + ": "
|
||||
// + this.client.getdXClusterMemberTable().get(key).getFrequency());
|
||||
//
|
||||
// }
|
||||
|
||||
// for (int i = 0; i < 100; i++) {
|
||||
//
|
||||
// System.out.print("\n");
|
||||
// }
|
||||
|
||||
/**
|
||||
* keeepalive start
|
||||
*/
|
||||
// ChatMessage keepAliveMSG = new ChatMessage();
|
||||
// keepAliveMSG.setMessageText("\r");
|
||||
// keepAliveMSG.setMessageDirectedToServer(true);
|
||||
//
|
||||
// System.out.println(new Utils4KST().time_generateCurrentMMDDhhmmTimeString() + " [UserAct]: Sending keepalive: "
|
||||
// + keepAliveMSG.getMessageText());
|
||||
// /**
|
||||
// * Sending keepalive
|
||||
// */
|
||||
// this.client.getMessageTXBus().add(keepAliveMSG);
|
||||
|
||||
/**
|
||||
* keeepalive end
|
||||
*/
|
||||
|
||||
// System.out.println("[UserAct]: Show the Userlist with known frequencies sorted now: ");
|
||||
|
||||
// ObservableList<ChatMember> userlist = this.client.getLst_chatMemberList();
|
||||
|
||||
// for (Iterator iterator = userlist.iterator(); iterator.hasNext();) {
|
||||
// ChatMember chatMember = (ChatMember) iterator.next();
|
||||
// System.out.println("[Useract] Entry " + this.client.getLst_chatMemberList().indexOf(chatMember) + ": " + chatMember.getCallSign());
|
||||
// }
|
||||
|
||||
//
|
||||
// String chatMembers ="";
|
||||
|
||||
// SortedSet<String> keys = new TreeSet<>(this.client.getChatMemberTable().keySet());
|
||||
// for (String key : keys) {
|
||||
//
|
||||
// chatMembers += this.client.getChatMemberTable().get(key).getCallSign() + ", "
|
||||
// + this.client.getChatMemberTable().get(key).getName() + " in "
|
||||
// + this.client.getChatMemberTable().get(key).getQra() + " @ QRG: "
|
||||
// + this.client.getChatMemberTable().get(key).getFrequency() + "\n";
|
||||
//
|
||||
// System.out.println(this.client.getChatMemberTable().get(key).getCallSign() + ", "
|
||||
// + this.client.getChatMemberTable().get(key).getName() + " in "
|
||||
// + this.client.getChatMemberTable().get(key).getQra() + " @ QRG: "
|
||||
// + this.client.getChatMemberTable().get(key).getFrequency());
|
||||
// }
|
||||
|
||||
// System.out.println("\n[UserAct]: Show the Clusterlist with known frequencies sorted now: ");
|
||||
|
||||
// String dxcMembers ="";
|
||||
|
||||
// SortedSet<String> keys2 = new TreeSet<>(this.client.getdXClusterMemberTable().keySet());
|
||||
// for (String key : keys2) {
|
||||
// System.out.println(this.client.getdXClusterMemberTable().get(key).getCallSign() + " in "
|
||||
// + this.client.getdXClusterMemberTable().get(key).getQra() + " @ QRG: "
|
||||
// + this.client.getdXClusterMemberTable().get(key).getFrequency());
|
||||
//
|
||||
// dxcMembers += this.client.getdXClusterMemberTable().get(key).getCallSign() + " in "
|
||||
// + this.client.getdXClusterMemberTable().get(key).getQra() + " @ QRG: "
|
||||
// + this.client.getdXClusterMemberTable().get(key).getFrequency();
|
||||
//
|
||||
// }
|
||||
|
||||
// File userListLogger = new File(new Utils4KST().time_generateCurrentMMddString() + "_praktiKST_userlist.txt");
|
||||
//
|
||||
// FileWriter fileWriterRAWChatMSGOut = null;
|
||||
//
|
||||
// try {
|
||||
// fileWriterRAWChatMSGOut = new FileWriter(userListLogger, true);
|
||||
// } catch (IOException e1) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e1.printStackTrace();
|
||||
// }
|
||||
//
|
||||
// BufferedWriter bufwrtrRawMSGOut;
|
||||
//
|
||||
// bufwrtrRawMSGOut = new BufferedWriter(fileWriterRAWChatMSGOut);
|
||||
|
||||
// System.out.println("#######################################" + chatMembers);
|
||||
// try {
|
||||
// bufwrtrRawMSGOut.write(new Utils4KST().time_generateCurrentMMDDhhmmTimeString() + " " +this.client.getChatMemberTable().size() + " Chatmembers:\n" + chatMembers+ "\n");
|
||||
// bufwrtrRawMSGOut.write(new Utils4KST().time_generateCurrentMMDDhhmmTimeString() + " " + this.client.getdXClusterMemberTable().size() + " Clusterentries:\n" + dxcMembers + "\n");
|
||||
|
||||
// bufwrtrRawMSGOut.flush();
|
||||
|
||||
// } catch (IOException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
|
||||
// try {
|
||||
// bufwrtrRawMSGOut.close();
|
||||
// } catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
}
|
||||
|
||||
private boolean createMissingLogFile(Path logFile) {
|
||||
if (Files.exists(logFile)) {
|
||||
if (!Files.isRegularFile(logFile)) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"The selected Simplelogfile path is not a regular file: {0}",
|
||||
logFile);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Files.createFile(logFile);
|
||||
LOGGER.log(Level.INFO, "Created missing Simplelogfile {0}.", logFile);
|
||||
return true;
|
||||
} catch (FileAlreadyExistsException exception) {
|
||||
return false;
|
||||
} catch (IOException | SecurityException exception) {
|
||||
LOGGER.log(Level.WARNING, "Cannot create Simplelogfile " + logFile, exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,9 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(
|
||||
Kst4ContestApplication.class.getName());
|
||||
private static final String SIMPLE_LOG_MANUAL_URL =
|
||||
"https://kst4contest.hamradioonline.de/manual/en/log-sync/"
|
||||
+ "#method-1-universal-file-based-callsign-interpreter-simplelogfile";
|
||||
|
||||
private boolean gridSquareHighlightEnabled = false;
|
||||
|
||||
@@ -12129,6 +12132,31 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSimpleLogFileCreated(Path filePath) {
|
||||
Alert alert = new Alert(AlertType.INFORMATION);
|
||||
alert.setTitle("Simplelogfile created");
|
||||
alert.setHeaderText("The selected Simplelogfile did not exist and has been created");
|
||||
|
||||
Label explanation = new Label(
|
||||
"File: " + filePath + "\n\n"
|
||||
+ "Configure your logging application to write its live log to this file. "
|
||||
+ "Then log a test QSO and verify that the callsign is marked as worked "
|
||||
+ "in KST4Contest within one minute.\n\n"
|
||||
+ "Before each contest, verify that the logging application writes the current "
|
||||
+ "contest log to this exact file. KST4Contest does not reset Simplelogfile-derived "
|
||||
+ "Worked marks automatically when a new contest starts.");
|
||||
explanation.setWrapText(true);
|
||||
|
||||
Hyperlink manualLink = new Hyperlink("Open the Simplelogfile manual");
|
||||
manualLink.setOnAction(event -> getHostServices().showDocument(SIMPLE_LOG_MANUAL_URL));
|
||||
|
||||
VBox content = new VBox(10, explanation, manualLink);
|
||||
content.setPrefWidth(560);
|
||||
alert.getDialogPane().setContent(content);
|
||||
alert.show();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Forces the station FilteredList to evaluate all active predicates again.
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import kst4contest.model.ChatMember;
|
||||
import kst4contest.model.ClusterMessage;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ChatControllerSimpleLogTest {
|
||||
|
||||
@Test
|
||||
void marksEveryActiveVariantOfWorkedBaseCallsign() {
|
||||
ChatMember categoryTwoVariant = member("9A0BB-2");
|
||||
ChatMember categoryThreeVariant = member("9A0BB-70");
|
||||
ChatMember unrelated = member("DL1ABC");
|
||||
|
||||
int changed = ChatController.markSimpleLogWorkedMembers(
|
||||
List.of(categoryTwoVariant, categoryThreeVariant, unrelated),
|
||||
Set.of("9A0BB"));
|
||||
|
||||
assertEquals(2, changed);
|
||||
assertTrue(categoryTwoVariant.isWorked());
|
||||
assertTrue(categoryThreeVariant.isWorked());
|
||||
assertFalse(unrelated.isWorked());
|
||||
}
|
||||
|
||||
@Test
|
||||
void marksClusterReceiverByBaseCallsignAndHandlesIncompleteMessages() {
|
||||
ClusterMessage matching = new ClusterMessage();
|
||||
matching.setReceiver(member("9A0BB-70"));
|
||||
ClusterMessage incomplete = new ClusterMessage();
|
||||
|
||||
int changed = ChatController.markSimpleLogWorkedClusterMessages(
|
||||
List.of(matching, incomplete), Set.of("9A0BB"));
|
||||
|
||||
assertEquals(1, changed);
|
||||
assertTrue(matching.isReceiverWkd());
|
||||
assertFalse(incomplete.isReceiverWkd());
|
||||
}
|
||||
|
||||
private static ChatMember member(String callSign) {
|
||||
ChatMember member = new ChatMember();
|
||||
member.setCallSign(callSign);
|
||||
return member;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anySet;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import kst4contest.model.ChatPreferences;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserActualizationTaskTest {
|
||||
|
||||
@TempDir
|
||||
Path temporaryDirectory;
|
||||
|
||||
@Mock
|
||||
ChatController controller;
|
||||
|
||||
@Mock
|
||||
ChatPreferences preferences;
|
||||
|
||||
@BeforeEach
|
||||
void configureControllerPreferences() {
|
||||
when(controller.getChatPreferences()).thenReturn(preferences);
|
||||
}
|
||||
|
||||
@Test
|
||||
void disabledInterpreterDoesNotCreateOrReadFile() {
|
||||
when(preferences.isLogsynch_fileBasedWkdCallInterpreterEnabled()).thenReturn(false);
|
||||
|
||||
new UserActualizationTask(controller).run();
|
||||
|
||||
verify(controller, never()).applySimpleLogWorkedBaseCalls(anySet());
|
||||
verify(controller, never()).notifySimpleLogFileCreated(org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsMissingFileAndNotifiesOnlyOnce() {
|
||||
Path logFile = temporaryDirectory.resolve("created.log").toAbsolutePath().normalize();
|
||||
when(preferences.isLogsynch_fileBasedWkdCallInterpreterEnabled()).thenReturn(true);
|
||||
when(preferences.getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly())
|
||||
.thenReturn(logFile.toString());
|
||||
UserActualizationTask task = new UserActualizationTask(controller);
|
||||
|
||||
task.run();
|
||||
task.run();
|
||||
|
||||
assertTrue(Files.isRegularFile(logFile));
|
||||
verify(controller, times(2)).applySimpleLogWorkedBaseCalls(anySet());
|
||||
verify(controller, times(1)).notifySimpleLogFileCreated(logFile);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readFailureIsContainedAndDoesNotUpdateUiState() {
|
||||
when(preferences.isLogsynch_fileBasedWkdCallInterpreterEnabled()).thenReturn(true);
|
||||
when(preferences.getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly())
|
||||
.thenReturn(temporaryDirectory.toString());
|
||||
|
||||
assertDoesNotThrow(() -> new UserActualizationTask(controller).run());
|
||||
|
||||
verify(controller, never()).applySimpleLogWorkedBaseCalls(anySet());
|
||||
verify(controller, never()).notifySimpleLogFileCreated(org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,48 @@
|
||||
package kst4contest.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
import kst4contest.controller.UCXLogFileToHashsetParser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
public class TestUCXLogFileToHashsetParser {
|
||||
class TestUCXLogFileToHashsetParser {
|
||||
|
||||
@TempDir
|
||||
Path temporaryDirectory;
|
||||
|
||||
@Test
|
||||
public static void main(String[] args) {
|
||||
// TODO Auto-generated method stub
|
||||
void parsesUniqueWorkedBaseCallsignsWithFixedPattern() throws IOException {
|
||||
Path logFile = temporaryDirectory.resolve("contest.log");
|
||||
Files.writeString(logFile, String.join(System.lineSeparator(),
|
||||
"QSO 001 S53CC 144 MHz",
|
||||
"QSO 002 9A0BB-70 432 MHz",
|
||||
"QSO 003 s53cc repeated",
|
||||
"no station here"));
|
||||
|
||||
UCXLogFileToHashsetParser testTheParser = new UCXLogFileToHashsetParser("C:\\UcxLog\\Logs\\DO5AMF\\DVU322_I.UCX");
|
||||
try {
|
||||
testTheParser.parse();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Set<String> result = new UCXLogFileToHashsetParser(logFile.toString()).parse();
|
||||
|
||||
assertEquals(Set.of("S53CC", "9A0BB"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void closesFileAfterEveryParsePass() throws IOException {
|
||||
Path logFile = temporaryDirectory.resolve("replaceable.log");
|
||||
Files.writeString(logFile, "QSO DL1ABC");
|
||||
|
||||
UCXLogFileToHashsetParser parser = new UCXLogFileToHashsetParser(logFile.toString());
|
||||
assertEquals(Set.of("DL1ABC"), parser.parse());
|
||||
|
||||
Path replacement = temporaryDirectory.resolve("replacement.log");
|
||||
Files.move(logFile, replacement);
|
||||
assertFalse(Files.exists(logFile));
|
||||
assertTrue(Files.exists(replacement));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: 💬
|
||||
category: ON4KST Chat
|
||||
since: "1.26"
|
||||
summary: Monitor two ON4KST categories in one interface while preserving the complete callsign and category required for correct message routing.
|
||||
description: KST4Contest combines two ON4KST chat sessions, keeps individual logins separate and shares station-related Worked, band and priority information through the normalised base callsign.
|
||||
description: KST4Contest uses one authenticated ON4KST TCP session for two chat categories, keeps their message contexts separate and shares station-related Worked, band and priority information through the normalised base callsign.
|
||||
tagsList:
|
||||
- ON4KST
|
||||
- dual chat
|
||||
@@ -29,43 +29,47 @@ VHF, UHF and microwave activity is not always concentrated in one ON4KST categor
|
||||
|
||||
Opening two unrelated chat clients would show both message streams, but every comparison between them would remain manual. Worked status, band information, active stations and sked context would still be distributed across separate windows.
|
||||
|
||||
KST4Contest therefore opens up to two ON4KST chat sessions and processes both in one operating context.
|
||||
KST4Contest therefore signs in once and adds a second chat category through ON4KST Single Sign-on when required. Both categories use the same TCP session and are processed in one operating context.
|
||||
|
||||

|
||||
|
||||
## Two connections remain two connections
|
||||
## One connection, two category contexts
|
||||
|
||||
The primary and secondary chat sessions retain their own:
|
||||
KST4Contest authenticates the ON4KST TCP session with one local login callsign and one password. The session uses one common locator for both categories; the locator is not an authentication credential. The primary category is part of the initial login. If a second category is enabled, the client adds it to the same session through Single Sign-on. It does not open another TCP connection or perform a second local login.
|
||||
|
||||
The primary and secondary categories still retain their own:
|
||||
|
||||
- ON4KST chat category;
|
||||
- login callsign;
|
||||
- visible category-specific **Name in Chat** field;
|
||||
- public message stream;
|
||||
- destination category for outgoing messages; and
|
||||
- active login entries.
|
||||
- message context and destination category for outgoing messages;
|
||||
- local QRG;
|
||||
- beacon enable setting and message template; and
|
||||
- active chat-member entries.
|
||||
|
||||
The second session can use the same local callsign as the primary session or a separately configured callsign. A different login may be useful when the station uses category- or band-specific suffixes.
|
||||
The **Name in Chat 2** field configures only the visible name field for the second category. It is neither a second login callsign nor a message destination. Both categories continue to use the one local login callsign and the common locator of the TCP session.
|
||||
|
||||
Combining the display does not turn both sessions into one ON4KST connection. Messages must still be sent through the category in which the intended destination is active.
|
||||
One connection therefore does not mean one combined message stream. Messages must still be sent in the category in which the intended destination is active.
|
||||
|
||||
## What identifies an active chat member?
|
||||
|
||||
An active login is identified by:
|
||||
For remote chat participants, an active member is identified by:
|
||||
|
||||
1. the complete visible callsign, including its suffix; and
|
||||
1. the complete visible callsign variant, including its suffix; and
|
||||
2. the ON4KST chat category.
|
||||
|
||||
Both parts are required.
|
||||
|
||||
Consider the following active entries:
|
||||
|
||||
| Complete callsign | Chat category | Active entity |
|
||||
| Complete visible callsign variant | Chat category | Active entity |
|
||||
|---|---:|---|
|
||||
| `9A0BB-2` | 1 | separate |
|
||||
| `9A0BB-70` | 1 | separate |
|
||||
| `9A0BB-23` | 2 | separate |
|
||||
| `9A0BB-13` | 2 | separate |
|
||||
|
||||
All four entries belong to the same base callsign, but they are four different chat logins. Joining, updating or leaving the chat affects only the corresponding complete callsign in the corresponding category.
|
||||
All four entries belong to the same base callsign, but they are four different chat-member identities. Joining, updating or leaving the chat affects only the corresponding complete callsign in the corresponding category.
|
||||
|
||||
This is particularly important for `9A0BB-2` and `9A0BB-70`: because both use the same category, the category alone cannot distinguish them.
|
||||
|
||||
@@ -73,7 +77,7 @@ This is particularly important for `9A0BB-2` and `9A0BB-70`: because both use th
|
||||
|
||||
## How are messages routed?
|
||||
|
||||
When a row is selected, KST4Contest retains both the complete callsign and its category. A private message is addressed to that complete callsign and sent through the corresponding chat session.
|
||||
When a row is selected, KST4Contest retains both the complete callsign and its category. A private message is addressed to that complete callsign and sent in the corresponding category within the shared session. Incoming private messages for the local station are addressed to the local login callsign, not to a category-specific **Name in Chat** field.
|
||||
|
||||
Selecting `9A0BB-70` in category 1 therefore creates a message for `9A0BB-70` in category 1. It is not silently reduced to `9A0BB`, and it is not sent through category 2 merely because another variant is active there.
|
||||
|
||||
@@ -86,11 +90,11 @@ The same distinction is used for:
|
||||
- sked reminder messages; and
|
||||
- leaving or updating an active chat entry.
|
||||
|
||||
The different variants do not have to be able to send messages to each other. That is not the purpose of the function. They must remain correctly reachable by other stations and receive the messages addressed to their respective login.
|
||||
The different variants do not have to be able to send messages to each other. That is not the purpose of the function. They must remain correctly reachable by other stations and receive the messages addressed to their respective chat-member identity.
|
||||
|
||||
## What is shared through the base callsign?
|
||||
|
||||
Some information describes the radio station rather than one temporary chat login. This information is aggregated through the normalised base callsign.
|
||||
Some information describes a remote radio station rather than one temporary chat-member identity. This information is aggregated through the normalised base callsign.
|
||||
|
||||
For the examples above, the common base callsign is `9A0BB`.
|
||||
|
||||
@@ -109,7 +113,7 @@ Per-band information is still retained. Working the station on 70 MHz does not m
|
||||
|
||||
## Why are band hints combined?
|
||||
|
||||
A station may use its visible login names to describe the available bands:
|
||||
A remote station may use complete visible callsign variants to describe the available bands:
|
||||
|
||||
- `9A0BB-2`
|
||||
- `9A0BB-70`
|
||||
@@ -124,13 +128,13 @@ A manual NOT-QRV mark still takes precedence. An automatically detected suffix o
|
||||
|
||||
## Why is the Priority Score not duplicated?
|
||||
|
||||
The Priority Score represents the operating priority of the station, not the number of chat logins it happens to use.
|
||||
The Priority Score represents the operating priority of the remote station, not the number of chat-member identities it happens to use.
|
||||
|
||||
KST4Contest therefore calculates the score for the normalised base callsign and projects it to the active variants. Several suffixes do not create several independent priority candidates merely by being logged in more than once.
|
||||
|
||||
The individual rows remain selectable because the correct message destination still matters.
|
||||
|
||||
In plain terms: one station should not occupy half the priority list, but the operator must still be able to contact the correct login.
|
||||
In plain terms: one station should not occupy half the priority list, but the operator must still be able to contact the correct chat-member identity.
|
||||
|
||||
## Categories are context, not proof of a band
|
||||
|
||||
@@ -150,7 +154,7 @@ Categories unrelated to the supported VHF, UHF, microwave or EME workflows are i
|
||||
|
||||
Sharing the station context does not merge everything associated with the base callsign.
|
||||
|
||||
The following remain tied to the individual login or message:
|
||||
The following remain tied to the individual remote chat member or message:
|
||||
|
||||
- complete message destination;
|
||||
- chat category;
|
||||
@@ -158,15 +162,15 @@ The following remain tied to the individual login or message:
|
||||
- visible callsign suffix;
|
||||
- join and leave state;
|
||||
- the specific row selected by the operator; and
|
||||
- message history associated with that login and category.
|
||||
- message history associated with that chat member and category.
|
||||
|
||||
This distinction is the basis of the dual-chat implementation: operational information may be shared where it describes the same radio station, while communication remains attached to the actual ON4KST login.
|
||||
This distinction is the basis of the dual-chat implementation: operational information may be shared where it describes the same remote radio station, while communication remains attached to the complete ON4KST callsign and category.
|
||||
|
||||
## Practical limitations
|
||||
|
||||
The shared base-callsign model assumes that suffix variants separated by `-` belong to the same underlying amateur-radio station. That is appropriate for common logins such as `-2`, `-70`, `-144` or `-432`.
|
||||
The shared base-callsign model assumes that complete visible callsign variants separated by `-` belong to the same underlying amateur-radio station. That is appropriate for variants such as `-2`, `-70`, `-144` or `-432`.
|
||||
|
||||
KST4Contest cannot determine whether two operators behind those logins are using one station, several independently operated stations or a distributed contest setup. Worked and band information is therefore shared at callsign level, while the operator remains responsible for selecting the correct chat destination.
|
||||
KST4Contest cannot determine whether two operators behind those chat entries are using one station, several independently operated stations or a distributed contest setup. Worked and band information is therefore shared at callsign level, while the operator remains responsible for selecting the correct chat destination.
|
||||
|
||||
[Read how both chat categories are configured.](/manual/en/configuration/#login-and-chat-categories)
|
||||
|
||||
|
||||
@@ -37,7 +37,9 @@ The available information depends on the interface:
|
||||
| General QSO UDP listener | yes | when included | when included |
|
||||
| Win-Test network listener | yes | yes | when included |
|
||||
|
||||
The Simplelogfile interpreter is broadly compatible but can only identify worked callsigns. A callsign match in a file does not provide enough information to infer a reliable band or grid square.
|
||||
The Simplelogfile interpreter reads the selected text file once per minute using a fixed callsign pattern. Matches are normalised to the base callsign and set the global Worked status for all active variants. A callsign match does not provide enough information to infer a reliable band or grid square.
|
||||
|
||||
If the file does not exist, KST4Contest creates it and displays the path together with the initial setup and contest checks. The file itself is the durable source. Its Worked marks are not stored in SQLite, are not removed automatically during the running session and are not reset automatically for a new contest.
|
||||
|
||||
The general UDP listener processes packets from UCXLog, N1MM+, QARTest and DXLog.net. Where the packet contains band and locator data, KST4Contest also updates the per-band Worked state and worked grid square.
|
||||
|
||||
@@ -47,7 +49,7 @@ The general UDP listener processes packets from UCXLog, N1MM+, QARTest and DXLog
|
||||
|
||||
Win-Test uses a separate listener for its native network protocol. KST4Contest resolves the Win-Test band ID, including 50 and 70 MHz, and stores the resulting Worked information in the same internal database.
|
||||
|
||||
STATUS packets can also update the local QRG. In multi-operator networks, a station-name filter prevents STATUS packets from another operating position from replacing the frequency of the intended radio.
|
||||
STATUS packets can also update the local QRG when QRG synchronisation is enabled and valid packets actually arrive. Enabling the source alone does not supply a frequency. In multi-operator networks, a station-name filter prevents STATUS packets from another operating position from replacing the frequency of the intended radio.
|
||||
|
||||
Win-Test can additionally receive skeds created in KST4Contest. The handover only takes place when a QRG matching the selected band can be determined. No fixed fallback frequency is inserted merely to make the packet technically valid.
|
||||
|
||||
@@ -55,7 +57,9 @@ Win-Test can additionally receive skeds created in KST4Contest. The handover onl
|
||||
|
||||
## Stored state and limitations
|
||||
|
||||
Worked, NOT-QRV and worked-grid information is stored in the internal SQLite database and restored after a restart. Contest-related records expire automatically after three days.
|
||||
Worked, NOT-QRV and worked-grid information received through the network interfaces, together with manual marks, is stored in the internal SQLite database and restored after a restart. Contest-related records expire automatically after three days.
|
||||
|
||||
Simplelogfile marks follow a different data flow: they remain runtime state derived from the selected file. The file is read again after a restart and has no automatic contest reset.
|
||||
|
||||
KST4Contest can only use the fields supplied by the selected interface. Missing band or locator data is not reconstructed from guesswork. This makes the result less complete in some cases, but also avoids turning an incomplete log packet into incorrect Worked information.
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ A public beacon has no selected remote station. It can therefore use only global
|
||||
|
||||

|
||||
|
||||
Each chat category has its own enable setting and message template. Both categories use the same timer interval. The second beacon is sent only when the second chat login and its beacon are enabled.
|
||||
Each chat category has its own enable setting and message template. Both categories use the same timer interval. The second beacon is sent only when the second category and its beacon are enabled in the shared ON4KST session.
|
||||
|
||||
Global variables are evaluated again on every timer run. A QRG changed by the logger can therefore be included in the next beacon without editing its template.
|
||||
|
||||
|
||||
@@ -72,13 +72,13 @@ The base callsign is used to decide whether a message should be shown in the mon
|
||||
|
||||
A message for `DN9APW-70` therefore remains a message for `DN9APW-70`. It is not redirected to `DN9APW`, `DN9APW-2` or another suffix.
|
||||
|
||||
This is the same distinction used elsewhere in KST4Contest: station-related information may be shared through the base callsign, while communication remains attached to the actual ON4KST login.
|
||||
This is the same distinction used elsewhere in KST4Contest: station-related information may be shared through the base callsign, while communication remains attached to the remote participant's complete callsign and category.
|
||||
|
||||
## Both chat categories are included
|
||||
|
||||
When two ON4KST categories are connected, the same monitoring list applies to both. There is no need to configure the station separately for each category.
|
||||
When two ON4KST categories are active in the same session, the same monitoring list applies to both. There is no need to configure the station separately for each category.
|
||||
|
||||
The category of the individual message remains part of its context. Combining the monitoring view does not merge the underlying chat connections.
|
||||
The category of the individual message remains part of its context. Combining the monitoring view does not merge the category-specific message streams or create separate chat connections.
|
||||
|
||||
## What the function does not do
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ G1YBB uses the map particularly consistently as a geographical contest worklist:
|
||||
|
||||
This creates a direct visual progression through the remaining stations. It is an optional and deliberately consistent operating method, not a prerequisite for using the map or KST4Contest.
|
||||
|
||||
[Watch G1YBB demonstrate this workflow.](https://www.youtube.com/watch?v=BCNCjowPgec)
|
||||
[Watch G1YBB demonstrate this workflow.](https://www.youtube.com/watch?v=lMQZMiSHlUI)
|
||||
|
||||
## What does the path analysis calculate?
|
||||
|
||||
|
||||
Reference in New Issue
Block a user