mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-08-23 18:47:34 +02:00
manual: updated beacon settings descriptin. Source: implemented plausibility checks for beacon texts
This commit is contained in:
@@ -605,13 +605,21 @@ Die Timeline ist eine Vorschau. AirScout-Daten können sich ändern, und ein ein
|
||||
|
||||
## Intervall-Beacon
|
||||
|
||||
KST4Contest kann wiederkehrende CQ-Nachrichten in den öffentlichen Chat senden. Beide Chat-Kategorien verwenden ein gemeinsames Intervall, besitzen aber jeweils einen eigenen Aktivierungsschalter und Nachrichtentext. Globale Variablen wie `MYQRG`, `SECONDQRG` oder `MYLOCATOR` werden unmittelbar vor jeder Aussendung aktualisiert.
|
||||
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.
|
||||
|
||||
Der Beacon ist für längeres CQ-Rufen auf einer festen Frequenz gedacht. Beim Absuchen oder häufigen Wechseln der QRG sollte er ausgeschaltet werden, damit keine inzwischen falsche Frequenz verbreitet wird. Details: [Konfiguration – Beacon Settings](de-Konfiguration#beacon-settings-automatischer-beacon).
|
||||
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.
|
||||
|
||||
Vor dem Versand prüft KST4Contest den vollständig aufgelösten Nachrichtentext. Leere Nachrichten, Zeilenumbrüche, das Protokoll-Trennzeichen `|` und Texte mit mehr als 120 Zeichen werden nicht gesendet.
|
||||
|
||||
Beim Absuchen oder häufigen Wechseln der QRG sollte der Beacon ausgeschaltet werden. Eine automatisch verbreitete Frequenz ist nur dann hilfreich, wenn dort tatsächlich jemand hört und ruft.
|
||||
|
||||
Konfiguration, Timer-Verhalten und verfügbare Variablen: [Konfiguration – Beacon Settings](de-Konfiguration#beacon-settings-automatischer-beacon).
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Simplelogfile
|
||||
|
||||
Details: [Log-Synchronisation](de-Log-Synchronisation#methode-1-universal-file-based-callsign-interpreter-simplelogfile).
|
||||
|
||||
@@ -533,7 +533,7 @@ Eine vollständige Übersicht der verfügbaren Platzhalter und ihrer Grenzen ste
|
||||
|
||||

|
||||
|
||||
Ein Beacon sendet in regelmäßigen Abständen eine öffentliche CQ-Nachricht. Er ist für Betriebssituationen gedacht, in denen die eigene Station über längere Zeit auf einer festen Frequenz ruft. Andere Stationen erhalten dadurch eine aktuelle QRG-Information, ohne dass der Operator denselben Text wiederholt von Hand in den Chat schreiben muss.
|
||||
Ein Beacon sendet in regelmäßigen Abständen eine öffentliche CQ-Nachricht. Er ist für Betriebssituationen gedacht, in denen die eigene Station über längere Zeit auf einer festen Frequenz ruft. Andere Stationen erhalten dadurch eine aktuelle QRG-Information, ohne dass der Operator denselben Text immer wieder von Hand in den Chat schreiben muss.
|
||||
|
||||
KST4Contest verwendet einen gemeinsamen Timer für beide Chat-Kategorien. Aktivierung und Nachrichtentext werden trotzdem getrennt konfiguriert:
|
||||
|
||||
@@ -547,21 +547,56 @@ Sind beide Beacons aktiviert, werden sie beim selben Timer-Lauf nacheinander in
|
||||
|
||||
Das Intervall wird in ganzen Minuten angegeben. Der kleinste zulässige Wert ist eine Minute.
|
||||
|
||||
Nach dem Aufbau der Chat-Verbindung prüft KST4Contest die Beacons erstmals nach ungefähr zehn Sekunden. Anschließend gilt das eingestellte Intervall. Wird der Wert während einer laufenden Verbindung geändert, beginnt der Countdown mit dem neuen Intervall erneut. Die Änderung selbst löst keine sofortige Nachricht aus.
|
||||
Nach dem Aufbau der Chat-Verbindung prüft KST4Contest die Beacons erstmals nach ungefähr zehn Sekunden. Anschließend gilt das eingestellte Intervall.
|
||||
|
||||
Wird das Intervall während einer laufenden Verbindung geändert, beginnt der Countdown mit dem neuen Wert erneut. Die Änderung selbst löst keine sofortige Beacon-Nachricht aus.
|
||||
|
||||
Beide Kategorien verwenden denselben Timer. Unterschiedliche Intervalle für den ersten und zweiten Chat können deshalb nicht eingestellt werden.
|
||||
|
||||
### Nachrichtentext und Variablen
|
||||
|
||||
Ein Beacon darf nach der Variablenauflösung höchstens 120 Zeichen enthalten. KST4Contest prüft deshalb nicht nur das eingetragene Template, sondern den tatsächlich zu sendenden Text.
|
||||
Ein Beacon darf die [globalen Variablen](de-Makros-und-Variablen#variablen-im-beacon) verwenden, die sich ausschließlich auf die eigene Station beziehen:
|
||||
|
||||
Im Beacon können alle [globalen Variablen](de-Makros-und-Variablen#variablen-im-beacon) verwendet werden, beispielsweise:
|
||||
- `MYQRG`
|
||||
- `MYQRGSHORT`
|
||||
- `SECONDQRG`
|
||||
- `MYLOCATOR`
|
||||
- `MYLOCATORSHORT`
|
||||
- `MYCALL`
|
||||
- `MYQTF`
|
||||
|
||||
Eine mögliche Nachricht für die erste Chat-Kategorie ist:
|
||||
|
||||
```text
|
||||
calling cq at MYQRG, ant MYQTF deg, loc MYLOCATOR
|
||||
calling cq at MYQRGSHORT, ant MYQTF deg, loc MYLOCATOR
|
||||
```
|
||||
|
||||
Die Variablen werden bei jedem Timer-Lauf neu aufgelöst. Ändert die Logsoftware zwischenzeitlich die in `MYQRG` gespeicherte Frequenz, verwendet bereits der nächste Beacon den neuen Wert.
|
||||
Für den zweiten Chat muss `SECONDQRG` verwendet werden, wenn dessen Frequenz von der ersten Kategorie abweicht:
|
||||
|
||||
Stationsbezogene Variablen wie `QRZNAME`, `FIRSTAP` oder `SECONDAP` benötigen dagegen eine ausgewählte Gegenstation. Da ein öffentlicher Beacon keine Gegenstation adressiert, werden diese Variablen im Beacon nicht aufgelöst.
|
||||
```text
|
||||
calling cq at SECONDQRG, ant MYQTF deg, loc MYLOCATOR
|
||||
```
|
||||
|
||||
Die Variablen werden bei jedem Timer-Lauf neu aufgelöst. Ändert die Logsoftware zwischenzeitlich die in `MYQRG` gespeicherte Frequenz, verwendet bereits der nächste Beacon den aktualisierten Wert. Das Template selbst muss dafür nicht geändert werden.
|
||||
|
||||
`MYQRG` und `MYQRGSHORT` beziehen sich immer auf die erste Chat-Kategorie. Die Auswahl oder Aktivierung des zweiten Chats ändert diese Zuordnung nicht.
|
||||
|
||||
Stationsbezogene Variablen wie `QRZNAME`, `FIRSTAP` oder `SECONDAP` benötigen eine ausgewählte Gegenstation. Da ein öffentlicher Beacon keine bestimmte Station adressiert, werden diese Variablen im Beacon nicht aufgelöst.
|
||||
|
||||
### Prüfung des Nachrichtentextes
|
||||
|
||||
KST4Contest prüft sowohl das eingetragene Template als auch den nach der Variablenauflösung tatsächlich zu sendenden Text.
|
||||
|
||||
Für Beacon-Nachrichten gelten folgende Bedingungen:
|
||||
|
||||
- Der endgültige Nachrichtentext darf nicht leer sein.
|
||||
- Er darf höchstens 120 Zeichen enthalten.
|
||||
- Das Protokoll-Trennzeichen `|` ist nicht zulässig.
|
||||
- Zeilenumbrüche sind nicht zulässig.
|
||||
|
||||
Eine ungültige Eingabe wird nicht als neue Beacon-Konfiguration übernommen. Wird ein Template erst durch eine spätere Variablenauflösung ungültig, beispielsweise weil der aufgelöste Text länger als 120 Zeichen ist, wird dieser Beacon-Lauf ausgelassen.
|
||||
|
||||
Ein Template, das ausschließlich aus einer momentan noch leeren globalen Variable besteht, kann gespeichert werden. Das ist beispielsweise beim Start möglich, bevor die erste QRG vom Logprogramm empfangen wurde. Solange die Variable keinen verwendbaren Inhalt liefert, sendet KST4Contest jedoch keine leere Nachricht.
|
||||
|
||||
### Wann sollte der Beacon ausgeschaltet werden?
|
||||
|
||||
@@ -570,6 +605,7 @@ Der Beacon ist nur dann hilfreich, wenn seine QRG-Angabe zum tatsächlichen Betr
|
||||
Im Klartext: Solange auf einer festen QRG CQ gerufen wird, spart der Beacon Arbeit. Beim „Schleichen“ über das Band sollte er ausgeschaltet werden.
|
||||
|
||||
Änderungen wirken während der laufenden Verbindung. Damit Aktivierung, Texte und Intervall auch nach dem nächsten Programmstart erhalten bleiben, anschließend **Save Settings** verwenden.
|
||||
|
||||
---
|
||||
|
||||
## Messagehandling Settings (ab v1.25)
|
||||
|
||||
@@ -166,13 +166,38 @@ Die globalen Variablen werden bei jedem Timer-Lauf neu ausgewertet. Dadurch kann
|
||||
Der vollständig aufgelöste Nachrichtentext darf höchstens 120 Zeichen enthalten. Weitere Angaben zum gemeinsamen Intervall und zum Verhalten beider Chat-Kategorien stehen unter [Konfiguration – Beacon Settings](de-Konfiguration#beacon-settings-automatischer-beacon).
|
||||
|
||||
---
|
||||
## Beispiel-Workflow mit Makros im Contest
|
||||
## Beispiel-Workflow mit Makros im Contest## Variablen im Beacon
|
||||
|
||||
1. Station in der Benutzerliste auswählen → Rufzeichen ist nun vorausgewählt.
|
||||
2. `Ctrl+1` drücken → Snippet „Hi OM, try sked?" wird als PM adressiert.
|
||||
3. Enter drücken → Nachricht wird gesendet.
|
||||
4. Station antwortet mit Frequenz → QRG-Spalte wird automatisch befüllt.
|
||||
5. `Ctrl+2` → Snippet „I am calling cq ur dir, pse lsn to me at 144.388" (MYQRG aufgelöst).
|
||||
6. Enter → Gesendet.
|
||||
Ein öffentlicher Beacon besitzt keine ausgewählte Gegenstation. Deshalb können hier ausschließlich Variablen sinnvoll verwendet werden, die von der eigenen Station und ihrer aktuellen Konfiguration abhängen:
|
||||
|
||||
Ohne manuelle Tipparbeit, ohne Fehler, ohne Unterbrechung des CQ-Rufens.
|
||||
| Variable | Wert im Beacon |
|
||||
|---|---|
|
||||
| `MYQRG` | aktuelle QRG der ersten Chat-Kategorie |
|
||||
| `MYQRGSHORT` | auf sieben Zeichen gekürzte QRG der ersten Kategorie |
|
||||
| `SECONDQRG` | aktuelle QRG der zweiten Chat-Kategorie |
|
||||
| `MYLOCATOR` | eigener vollständiger Locator |
|
||||
| `MYLOCATORSHORT` | eigener vierstelliger Locator |
|
||||
| `MYCALL` | eigenes Rufzeichen |
|
||||
| `MYQTF` | aktuelle Antennenrichtung |
|
||||
|
||||
`QRZNAME`, `FIRSTAP` und `SECONDAP` benötigen eine ausgewählte Gegenstation. In einem öffentlichen Beacon werden sie daher nicht aufgelöst.
|
||||
|
||||
Eine mögliche Konfiguration für die erste Kategorie ist:
|
||||
|
||||
```text
|
||||
calling cq at MYQRGSHORT, ant MYQTF deg, loc MYLOCATOR
|
||||
```
|
||||
|
||||
Für den Beacon der zweiten Kategorie muss `SECONDQRG` verwendet werden, wenn dort eine andere Frequenz veröffentlicht werden soll:
|
||||
|
||||
```text
|
||||
calling cq at SECONDQRG, ant MYQTF deg, loc MYLOCATOR
|
||||
```
|
||||
|
||||
Die globalen Variablen werden bei jedem Timer-Lauf neu ausgewertet. Dadurch kann eine vom Logprogramm aktualisierte QRG bereits in der nächsten Beacon-Nachricht erscheinen.
|
||||
|
||||
Der vollständig aufgelöste Text muss mindestens ein gültiges Zeichen enthalten und darf höchstens 120 Zeichen lang sein. Das Protokoll-Trennzeichen `|` und Zeilenumbrüche sind nicht zulässig. Ist der Text beim vorgesehenen Versand noch leer oder ungültig, wird dieser Beacon-Lauf ausgelassen.
|
||||
|
||||
Weitere Angaben zum gemeinsamen Intervall und zum Verhalten beider Chat-Kategorien stehen unter [Konfiguration – Beacon Settings](de-Konfiguration#beacon-settings-automatischer-beacon).
|
||||
|
||||
---
|
||||
|
||||
@@ -549,9 +549,80 @@ The complete list of available placeholders and their limitations is described u
|
||||
|
||||
## Beacon Settings
|
||||
|
||||
Configuration of an automatic interval beacon in the public chat channel. Recommended: use the `MYQRG` variable in the text so the current frequency is always up to date. Interval and text are freely configurable.
|
||||

|
||||
|
||||
> **Tip**: Enable the beacon when calling CQ and quickly disable it in the settings window when not calling.
|
||||
A beacon sends a public CQ message at regular intervals. It is intended for operating situations in which the local station calls CQ on a fixed frequency for an extended period. Other stations receive current QRG information without requiring the operator to enter the same message repeatedly.
|
||||
|
||||
KST4Contest uses one shared timer for both chat categories. Each category nevertheless has its own enable setting and message template:
|
||||
|
||||
- **Enable CQ beacon** enables the beacon for the respective category.
|
||||
- **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.
|
||||
|
||||
### Interval and timer behaviour
|
||||
|
||||
The interval is entered in whole minutes. The minimum permitted value is one minute.
|
||||
|
||||
After the chat connection has been established, KST4Contest performs the first beacon check after approximately ten seconds. The configured interval applies after that initial check.
|
||||
|
||||
Changing the interval while connected restarts the countdown with the new value. The change itself does not cause an immediate beacon message.
|
||||
|
||||
Both categories use the same timer. Separate intervals for the primary and secondary chat cannot be configured.
|
||||
|
||||
### Message text and variables
|
||||
|
||||
A beacon may use the [global variables](en-Macros-and-Variables#variables-in-the-beacon) which depend only on the local station:
|
||||
|
||||
- `MYQRG`
|
||||
- `MYQRGSHORT`
|
||||
- `SECONDQRG`
|
||||
- `MYLOCATOR`
|
||||
- `MYLOCATORSHORT`
|
||||
- `MYCALL`
|
||||
- `MYQTF`
|
||||
|
||||
A suitable message for the primary chat category is:
|
||||
|
||||
```text
|
||||
calling cq at MYQRGSHORT, ant MYQTF deg, loc MYLOCATOR
|
||||
```
|
||||
|
||||
Use `SECONDQRG` for the second chat if it operates on a different frequency:
|
||||
|
||||
```text
|
||||
calling cq at SECONDQRG, ant MYQTF deg, loc MYLOCATOR
|
||||
```
|
||||
|
||||
Variables are resolved again on every timer run. If the logging software changes the QRG stored in `MYQRG`, the next beacon can already contain the updated value. The message template does not have to be edited.
|
||||
|
||||
`MYQRG` and `MYQRGSHORT` always refer to the primary chat category. Enabling or selecting the second chat does not change this assignment.
|
||||
|
||||
Station-specific variables such as `QRZNAME`, `FIRSTAP` and `SECONDAP` require a selected remote station. A public beacon has no such destination, so these variables are not resolved in beacon messages.
|
||||
|
||||
### Message validation
|
||||
|
||||
KST4Contest validates both the configured template and the message which remains after all variables have been resolved.
|
||||
|
||||
The following restrictions apply:
|
||||
|
||||
- The final message must not be empty.
|
||||
- It must not exceed 120 characters.
|
||||
- The protocol separator `|` is not permitted.
|
||||
- Line breaks are not permitted.
|
||||
|
||||
An invalid entry is not accepted as the new beacon configuration. If a template becomes invalid only after resolving its variables, for example because the resulting text exceeds 120 characters, that beacon run is skipped.
|
||||
|
||||
A template which consists only of a temporarily empty global variable may still be stored. This can happen during startup before the first QRG has been received from the logger. KST4Contest does not send an empty message while the variable has no usable value.
|
||||
|
||||
### When should the beacon be disabled?
|
||||
|
||||
The beacon is useful only while its QRG matches the actual operation. Leaving it enabled while searching the band or changing frequencies frequently may cause other stations to look for the local station on an obsolete frequency.
|
||||
|
||||
In plain terms: the beacon saves work while calling CQ on a fixed QRG. It should be disabled while moving around the band.
|
||||
|
||||
Changes take effect during the current connection. Use **Save Settings** afterwards to retain the enable settings, message templates and interval for the next program start.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -503,10 +503,21 @@ The timeline is a preview. AirScout data can change, and a stored sked guarantee
|
||||
|
||||
## Interval Beacon
|
||||
|
||||
Automatic CQ messages in the public channel at a configurable interval. Recommended: use the `MYQRG` variable so the current frequency is always accurate. Details: [Configuration – Beacon Settings](en-Configuration#beacon-settings).
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Before transmission, KST4Contest validates the completely resolved message. Empty messages, line breaks, the protocol separator `|` and messages exceeding 120 characters are not sent.
|
||||
|
||||
Disable the beacon while searching the band or changing QRG frequently. An automatically published frequency is useful only while somebody is actually listening and calling there.
|
||||
|
||||
Configuration, timer behaviour and available variables: [Configuration – Beacon Settings](en-Configuration#beacon-settings).
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Simplelogfile
|
||||
|
||||
File-based log evaluation using regex. Details: [Log Synchronisation](en-Log-Sync#method-1-universal-file-based-callsign-interpreter-simplelogfile).
|
||||
|
||||
@@ -140,16 +140,41 @@ Replaced by the current antenna direction in words (e.g. `north`, `north east`,
|
||||
|
||||
## Variables in the Beacon
|
||||
|
||||
All variables can also be used in the **automatic beacon** (interval messages). Recommended beacon configuration:
|
||||
A public beacon has no selected remote station. It can therefore make meaningful use only of variables which depend on the local station and its current configuration:
|
||||
|
||||
```
|
||||
calling cq at MYQRG, loc MYLOCATOR, GL all!
|
||||
| Variable | Value used in the beacon |
|
||||
|---|---|
|
||||
| `MYQRG` | current QRG of the primary chat category |
|
||||
| `MYQRGSHORT` | first seven characters of the primary QRG |
|
||||
| `SECONDQRG` | current QRG of the second chat category |
|
||||
| `MYLOCATOR` | complete configured locator of the local station |
|
||||
| `MYLOCATORSHORT` | four-character locator of the local station |
|
||||
| `MYCALL` | configured local callsign |
|
||||
| `MYQTF` | current antenna heading |
|
||||
|
||||
`QRZNAME`, `FIRSTAP` and `SECONDAP` require a selected remote station. They are therefore not resolved in a public beacon.
|
||||
|
||||
A suitable configuration for the primary category is:
|
||||
|
||||
```text
|
||||
calling cq at MYQRGSHORT, ant MYQTF deg, loc MYLOCATOR
|
||||
```
|
||||
|
||||
Since KST4Contest automatically reads QRG data from chat messages: if other stations also use KST4Contest, they will immediately see your QRG in the QRG column of their user list.
|
||||
For the second category, use `SECONDQRG` if that category should publish a different frequency:
|
||||
|
||||
```text
|
||||
calling cq at SECONDQRG, ant MYQTF deg, loc MYLOCATOR
|
||||
```
|
||||
|
||||
Global variables are evaluated again on every timer run. A QRG updated by the logging software can therefore appear in the next beacon message.
|
||||
|
||||
The completely resolved text must contain at least one valid character and must not exceed 120 characters. The protocol separator `|` and line breaks are not permitted. If the text is still empty or invalid when transmission is due, that beacon run is skipped.
|
||||
|
||||
The common interval and the behaviour of both chat categories are described under [Configuration – Beacon Settings](en-Configuration#beacon-settings).
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Example Contest Workflow with Macros
|
||||
|
||||
1. Select a station in the user list → callsign is now pre-selected.
|
||||
|
||||
@@ -2,6 +2,7 @@ package kst4contest.controller;
|
||||
|
||||
import java.util.TimerTask;
|
||||
|
||||
import kst4contest.model.ChatCategory;
|
||||
import kst4contest.model.ChatMessage;
|
||||
import kst4contest.model.ThreadStateMessage;
|
||||
|
||||
@@ -12,6 +13,10 @@ import kst4contest.model.ThreadStateMessage;
|
||||
* interval. Their enable flags and message templates remain independent. Every
|
||||
* run reads the current preferences, resolves global message variables and
|
||||
* sends only the categories which are currently enabled.</p>
|
||||
*
|
||||
* <p>Beacon messages use the regular outbound chat-message pipeline. They are
|
||||
* not assembled as raw ON4KST frames, because that would bypass the common
|
||||
* category, delimiter and message-text validation.</p>
|
||||
*/
|
||||
public class BeaconTask extends TimerTask {
|
||||
|
||||
@@ -39,35 +44,40 @@ public class BeaconTask extends TimerTask {
|
||||
Thread.currentThread().setName("BeaconTask");
|
||||
reportStatus(THREAD_NICKNAME, true, "initialized", false);
|
||||
|
||||
MessageVariableResolver variableResolver =
|
||||
new MessageVariableResolver(chatController.getChatPreferences());
|
||||
|
||||
sendMainCategoryBeacon(variableResolver);
|
||||
sendSecondCategoryBeacon(variableResolver);
|
||||
sendMainCategoryBeacon();
|
||||
sendSecondCategoryBeacon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the main-category beacon if it is currently enabled.
|
||||
*/
|
||||
private void sendMainCategoryBeacon(MessageVariableResolver variableResolver) {
|
||||
if (!chatController.getChatPreferences().isBcn_beaconsEnabledMainCat()) {
|
||||
reportStatus(THREAD_NICKNAME + " 1", false, "off", false);
|
||||
private void sendMainCategoryBeacon() {
|
||||
if (!chatController.getChatPreferences()
|
||||
.isBcn_beaconsEnabledMainCat()) {
|
||||
reportStatus(
|
||||
THREAD_NICKNAME + " 1",
|
||||
false,
|
||||
"off",
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
String resolvedText = variableResolver.resolveGlobalVariables(
|
||||
chatController.getChatPreferences().getBcn_beaconTextMainCat()
|
||||
);
|
||||
ChatMessage beaconMessage = buildBeaconMessage(
|
||||
chatController.getChatPreferences()
|
||||
.getLoginChatCategoryMain()
|
||||
.getCategoryNumber(),
|
||||
resolvedText,
|
||||
.getLoginChatCategoryMain(),
|
||||
chatController.getChatPreferences()
|
||||
.getBcn_beaconTextMainCat(),
|
||||
"main category"
|
||||
);
|
||||
|
||||
if (beaconMessage == null) {
|
||||
reportStatus(THREAD_NICKNAME + " 1", false, "invalid text", true);
|
||||
reportStatus(
|
||||
THREAD_NICKNAME + " 1",
|
||||
false,
|
||||
"invalid text",
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,37 +86,50 @@ public class BeaconTask extends TimerTask {
|
||||
+ " [BeaconTask, Info]: Sending main-category CQ: "
|
||||
+ beaconMessage.getMessageText()
|
||||
);
|
||||
|
||||
chatController.getMessageTXBus().add(beaconMessage);
|
||||
reportStatus(THREAD_NICKNAME + " 1", true, "on", false);
|
||||
|
||||
reportStatus(
|
||||
THREAD_NICKNAME + " 1",
|
||||
true,
|
||||
"on",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the second-category beacon if the second login and its beacon are
|
||||
* currently enabled.
|
||||
*/
|
||||
private void sendSecondCategoryBeacon(
|
||||
MessageVariableResolver variableResolver
|
||||
) {
|
||||
if (!chatController.getChatPreferences().isLoginToSecondChatEnabled()
|
||||
private void sendSecondCategoryBeacon() {
|
||||
if (!chatController.getChatPreferences()
|
||||
.isLoginToSecondChatEnabled()
|
||||
|| !chatController.getChatPreferences()
|
||||
.isBcn_beaconsEnabledSecondCat()) {
|
||||
reportStatus(THREAD_NICKNAME + " 2", false, "off", false);
|
||||
reportStatus(
|
||||
THREAD_NICKNAME + " 2",
|
||||
false,
|
||||
"off",
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
String resolvedText = variableResolver.resolveGlobalVariables(
|
||||
chatController.getChatPreferences().getBcn_beaconTextSecondCat()
|
||||
);
|
||||
ChatMessage beaconMessage = buildBeaconMessage(
|
||||
chatController.getChatPreferences()
|
||||
.getLoginChatCategorySecond()
|
||||
.getCategoryNumber(),
|
||||
resolvedText,
|
||||
.getLoginChatCategorySecond(),
|
||||
chatController.getChatPreferences()
|
||||
.getBcn_beaconTextSecondCat(),
|
||||
"second category"
|
||||
);
|
||||
|
||||
if (beaconMessage == null) {
|
||||
reportStatus(THREAD_NICKNAME + " 2", false, "invalid text", true);
|
||||
reportStatus(
|
||||
THREAD_NICKNAME + " 2",
|
||||
false,
|
||||
"invalid text",
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,47 +138,65 @@ public class BeaconTask extends TimerTask {
|
||||
+ " [BeaconTask, Info]: Sending second-category CQ: "
|
||||
+ beaconMessage.getMessageText()
|
||||
);
|
||||
|
||||
chatController.getMessageTXBus().add(beaconMessage);
|
||||
reportStatus(THREAD_NICKNAME + " 2", true, "on", false);
|
||||
|
||||
reportStatus(
|
||||
THREAD_NICKNAME + " 2",
|
||||
true,
|
||||
"on",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the server-directed message after validating the resolved payload.
|
||||
* Resolves and validates one beacon before placing it in the regular outbound
|
||||
* message queue.
|
||||
*
|
||||
* <p>The resolved text is checked rather than only the configured template
|
||||
* because inserted values can increase the final message length.</p>
|
||||
* <p>The returned message contains only the public-chat payload and its chat
|
||||
* category. {@link WriteThread} creates the final ON4KST frame through
|
||||
* {@link On4KstProtocol#chatMessage(int, String)}. This prevents a configurable
|
||||
* beacon text from bypassing the common protocol validation.</p>
|
||||
*
|
||||
* @param categoryNumber ON4KST category number
|
||||
* @param resolvedText fully resolved beacon payload
|
||||
* @param category target ON4KST chat category
|
||||
* @param configuredText configured beacon template
|
||||
* @param categoryDescription text used in diagnostic output
|
||||
* @return prepared message, or {@code null} if the payload is invalid
|
||||
* @return prepared message, or {@code null} if the category or text is invalid
|
||||
*/
|
||||
private ChatMessage buildBeaconMessage(
|
||||
int categoryNumber,
|
||||
String resolvedText,
|
||||
ChatCategory category,
|
||||
String configuredText,
|
||||
String categoryDescription
|
||||
) {
|
||||
if (resolvedText == null
|
||||
|| resolvedText.length() > ChatController.MAX_BEACON_TEXT_LENGTH) {
|
||||
int actualLength = resolvedText == null ? 0 : resolvedText.length();
|
||||
try {
|
||||
if (category == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"No chat category is configured."
|
||||
);
|
||||
}
|
||||
|
||||
On4KstProtocol.category(category.getCategoryNumber());
|
||||
|
||||
String resolvedText =
|
||||
chatController.resolveAndValidateBeaconText(
|
||||
configuredText
|
||||
);
|
||||
|
||||
ChatMessage beaconMessage = new ChatMessage();
|
||||
beaconMessage.setMessageText(resolvedText);
|
||||
beaconMessage.setChatCategory(category);
|
||||
beaconMessage.setMessageDirectedToServer(false);
|
||||
|
||||
return beaconMessage;
|
||||
} catch (IllegalArgumentException exception) {
|
||||
System.out.println(
|
||||
"[BeaconTask, Warning]: Beacon for "
|
||||
+ categoryDescription
|
||||
+ " was not sent because the resolved text contains "
|
||||
+ actualLength
|
||||
+ " characters; maximum is "
|
||||
+ ChatController.MAX_BEACON_TEXT_LENGTH
|
||||
+ "."
|
||||
+ " was not queued: "
|
||||
+ exception.getMessage()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
ChatMessage beaconMessage = new ChatMessage();
|
||||
beaconMessage.setMessageText(
|
||||
"MSG|" + categoryNumber + "|0|" + resolvedText + "|0|"
|
||||
);
|
||||
beaconMessage.setMessageDirectedToServer(true);
|
||||
return beaconMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,6 +214,9 @@ public class BeaconTask extends TimerTask {
|
||||
information,
|
||||
criticalState
|
||||
);
|
||||
callbackToController.onThreadStatus(THREAD_NICKNAME, stateMessage);
|
||||
callbackToController.onThreadStatus(
|
||||
THREAD_NICKNAME,
|
||||
stateMessage
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2827,6 +2827,82 @@ private ObservableList<String>
|
||||
this.dbHandler = dbHandler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates a configured beacon template before it is stored.
|
||||
*
|
||||
* <p>The template itself must be a non-empty, protocol-safe message. Global
|
||||
* variables are resolved as far as their values are currently available.
|
||||
* A template which temporarily resolves to an empty string, for example
|
||||
* {@code MYQRG} before a frequency is known, remains valid. The timer performs
|
||||
* the stricter final validation immediately before transmission.</p>
|
||||
*
|
||||
* @param configuredText configured beacon template
|
||||
* @throws IllegalArgumentException if the template contains invalid protocol
|
||||
* content or currently resolves to more than
|
||||
* {@value MAX_BEACON_TEXT_LENGTH} characters
|
||||
*/
|
||||
public void validateBeaconTemplate(String configuredText) {
|
||||
String normalizedTemplate =
|
||||
On4KstProtocol.messageText(configuredText);
|
||||
|
||||
String resolvedText =
|
||||
new MessageVariableResolver(chatPreferences)
|
||||
.resolveGlobalVariables(normalizedTemplate);
|
||||
|
||||
/*
|
||||
* A template consisting only of a variable such as MYQRG may temporarily
|
||||
* resolve to an empty value. It can still be stored because the value may
|
||||
* become available through TRX synchronisation before the timer runs.
|
||||
*/
|
||||
if (resolvedText == null || resolvedText.isBlank()) {
|
||||
return;
|
||||
}
|
||||
|
||||
validateResolvedBeaconText(resolvedText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves and validates the final beacon text immediately before transmission.
|
||||
*
|
||||
* <p>This is deliberately stricter than {@link #validateBeaconTemplate(String)}.
|
||||
* A timer run must never queue an empty message merely because a dynamic value
|
||||
* is not available at that moment.</p>
|
||||
*
|
||||
* @param configuredText configured beacon template
|
||||
* @return normalized text which may safely pass through the regular ON4KST
|
||||
* message pipeline
|
||||
* @throws IllegalArgumentException if the resolved message is empty, too long
|
||||
* or contains an ON4KST delimiter
|
||||
*/
|
||||
public String resolveAndValidateBeaconText(String configuredText) {
|
||||
String resolvedText =
|
||||
new MessageVariableResolver(chatPreferences)
|
||||
.resolveGlobalVariables(configuredText);
|
||||
|
||||
return validateResolvedBeaconText(resolvedText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the message-text and length rules to a fully resolved beacon.
|
||||
*/
|
||||
private String validateResolvedBeaconText(String resolvedText) {
|
||||
String normalizedText =
|
||||
On4KstProtocol.messageText(resolvedText);
|
||||
|
||||
if (normalizedText.length() > MAX_BEACON_TEXT_LENGTH) {
|
||||
throw new IllegalArgumentException(
|
||||
"The resolved beacon message contains "
|
||||
+ normalizedText.length()
|
||||
+ " characters; maximum is "
|
||||
+ MAX_BEACON_TEXT_LENGTH
|
||||
+ "."
|
||||
);
|
||||
}
|
||||
|
||||
return normalizedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the shared beacon timer with the interval currently stored in the
|
||||
* preferences.
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@@ -7,6 +10,8 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import kst4contest.model.ChatPreferences;
|
||||
|
||||
|
||||
class On4KstProtocolTest {
|
||||
@Test
|
||||
@@ -66,4 +71,72 @@ class On4KstProtocolTest {
|
||||
On4KstConnectionManager.parseMessageTimestamp(
|
||||
"CH|2|20260813123456|DL1ABC|Op|0|msg|0|"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvesBeaconVariablesBeforeApplyingProtocolValidation() {
|
||||
ChatPreferences preferences = new ChatPreferences();
|
||||
preferences.setMYQRGFirstCat("144.300");
|
||||
|
||||
ChatController controller = new ChatController();
|
||||
controller.setChatPreferences(preferences);
|
||||
|
||||
controller.validateBeaconTemplate(
|
||||
"calling cq at MYQRG"
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
"calling cq at 144.300",
|
||||
controller.resolveAndValidateBeaconText(
|
||||
"calling cq at MYQRG"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsTemporarilyUnresolvedVariableOnlyBeaconTemplate() {
|
||||
ChatPreferences preferences = new ChatPreferences();
|
||||
preferences.setMYQRGFirstCat("");
|
||||
|
||||
ChatController controller = new ChatController();
|
||||
controller.setChatPreferences(preferences);
|
||||
|
||||
assertDoesNotThrow(
|
||||
() -> controller.validateBeaconTemplate("MYQRG")
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> controller.resolveAndValidateBeaconText("MYQRG")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsEmptyOverlongAndProtocolBreakingBeaconText() {
|
||||
ChatPreferences preferences = new ChatPreferences();
|
||||
|
||||
ChatController controller = new ChatController();
|
||||
controller.setChatPreferences(preferences);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> controller.validateBeaconTemplate(" ")
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> controller.validateBeaconTemplate(
|
||||
"cq at 144.300|0|QUIT"
|
||||
)
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> controller.validateBeaconTemplate(
|
||||
"x".repeat(
|
||||
ChatController.MAX_BEACON_TEXT_LENGTH
|
||||
+ 1
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4933,41 +4933,52 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores one beacon text.
|
||||
* Validates and stores one beacon template.
|
||||
*
|
||||
* <p>The final text is checked after global variables have been resolved.
|
||||
* Otherwise a template with at most 120 characters could still exceed the
|
||||
* server limit after values such as MYQRG or MYLOCATOR were inserted.</p>
|
||||
* <p>The template is checked against the same protocol and length rules which
|
||||
* are applied by the timer before transmission. A variable-only template may
|
||||
* temporarily resolve to an empty value and can still be stored; the timer will
|
||||
* not send it until a usable value is available.</p>
|
||||
*
|
||||
* @param textField field containing the configured beacon template
|
||||
* @param mainCategory {@code true} for the main category, {@code false} for
|
||||
* the optional second category
|
||||
*/
|
||||
private void applyBeaconTextSetting(TextField textField, boolean mainCategory) {
|
||||
String configuredText = textField.getText() == null ? "" : textField.getText();
|
||||
String resolvedText = messageVariableResolver.resolveGlobalVariables(configuredText);
|
||||
private void applyBeaconTextSetting(
|
||||
TextField textField,
|
||||
boolean mainCategory
|
||||
) {
|
||||
String configuredText =
|
||||
textField.getText() == null
|
||||
? ""
|
||||
: textField.getText();
|
||||
|
||||
try {
|
||||
chatcontroller.validateBeaconTemplate(configuredText);
|
||||
|
||||
if (resolvedText != null
|
||||
&& resolvedText.length() <= ChatController.MAX_BEACON_TEXT_LENGTH) {
|
||||
if (mainCategory) {
|
||||
chatcontroller.getChatPreferences().setBcn_beaconTextMainCat(configuredText);
|
||||
chatcontroller.getChatPreferences()
|
||||
.setBcn_beaconTextMainCat(configuredText);
|
||||
} else {
|
||||
chatcontroller.getChatPreferences().setBcn_beaconTextSecondCat(configuredText);
|
||||
chatcontroller.getChatPreferences()
|
||||
.setBcn_beaconTextSecondCat(configuredText);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String previousText = mainCategory
|
||||
? chatcontroller.getChatPreferences().getBcn_beaconTextMainCat()
|
||||
: chatcontroller.getChatPreferences().getBcn_beaconTextSecondCat();
|
||||
} catch (IllegalArgumentException exception) {
|
||||
String previousText =
|
||||
mainCategory
|
||||
? chatcontroller.getChatPreferences()
|
||||
.getBcn_beaconTextMainCat()
|
||||
: chatcontroller.getChatPreferences()
|
||||
.getBcn_beaconTextSecondCat();
|
||||
|
||||
textField.setText(previousText);
|
||||
|
||||
alertWindowEvent(
|
||||
"The resolved beacon message must not exceed "
|
||||
+ ChatController.MAX_BEACON_TEXT_LENGTH
|
||||
+ " characters."
|
||||
"The beacon message is invalid: "
|
||||
+ exception.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and applies the shared interval used by both beacon categories.
|
||||
|
||||
Reference in New Issue
Block a user