Wintest improvements sked and options (#32)

* Improve Win-Test Integration in UI

* Rename Building Artifacts

* Document Changes

* Win-Test Fix QRG Sked
This commit is contained in:
Rsclub2_2 authored and GitHub committed 2026-04-19 13:00:26 +02:00
1 parent 071ea800ae
commit e35dee55d1
11 files changed
+439 -129

No files matched your search

+18 -19
View File
@@ -4,7 +4,6 @@ on:
push:
branches:
- main
- test-mac
schedule:
- cron: "20 2 * * *"
workflow_dispatch:
@@ -86,7 +85,7 @@ jobs:
SHORT_SHA="${GITHUB_SHA::7}"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "SHORT_SHA=$SHORT_SHA" >> "$GITHUB_ENV"
echo "ASSET_BASENAME=praktiKST-${VERSION}-${SHORT_SHA}" >> "$GITHUB_ENV"
echo "ASSET_BASENAME=KST4Contest-${VERSION}-${SHORT_SHA}" >> "$GITHUB_ENV"
- name: Set up Java 17
uses: actions/setup-java@v4.1.0
@@ -107,7 +106,7 @@ jobs:
mkdir -p dist
jpackage \
--type app-image \
--name praktiKST \
--name KST4Contest \
--input target/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
@@ -117,41 +116,41 @@ jobs:
- name: Create AppDir metadata
run: |
rm -rf target/praktiKST.AppDir
cp -a dist/praktiKST target/praktiKST.AppDir
rm -rf target/KST4Contest.AppDir
cp -a dist/KST4Contest target/KST4Contest.AppDir
cat > target/praktiKST.AppDir/AppRun << 'EOF'
cat > target/KST4Contest.AppDir/AppRun << 'EOF'
#!/bin/sh
HERE="$(dirname "$(readlink -f "$0")")"
exec "$HERE/bin/praktiKST" "$@"
exec "$HERE/bin/KST4Contest" "$@"
EOF
chmod +x target/praktiKST.AppDir/AppRun
chmod +x target/KST4Contest.AppDir/AppRun
cat > target/praktiKST.AppDir/praktiKST.desktop << 'EOF'
cat > target/KST4Contest.AppDir/KST4Contest.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=praktiKST
Exec=praktiKST
Icon=praktiKST
Name=KST4Contest
Exec=KST4Contest
Icon=KST4Contest
Categories=Network;HamRadio;
Terminal=false
EOF
if [ -f target/praktiKST.AppDir/lib/praktiKST.png ]; then
cp target/praktiKST.AppDir/lib/praktiKST.png target/praktiKST.AppDir/praktiKST.png
if [ -f target/KST4Contest.AppDir/lib/KST4Contest.png ]; then
cp target/KST4Contest.AppDir/lib/KST4Contest.png target/KST4Contest.AppDir/KST4Contest.png
fi
- name: Build AppImage
run: |
wget -q -O target/appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x target/appimagetool.AppImage
APPIMAGE_EXTRACT_AND_RUN=1 ARCH=x86_64 target/appimagetool.AppImage target/praktiKST.AppDir "dist/${ASSET_BASENAME}-linux-x86_64.AppImage"
APPIMAGE_EXTRACT_AND_RUN=1 ARCH=x86_64 target/appimagetool.AppImage target/KST4Contest.AppDir "dist/${ASSET_BASENAME}-linux-x86_64.AppImage"
- name: Upload Linux artifact
uses: actions/upload-artifact@v4.3.4
with:
name: linux-appimage
path: dist/praktiKST-*-linux-x86_64.AppImage
path: dist/KST4Contest-*-linux-x86_64.AppImage
retention-days: 14
build-macos-dmg:
@@ -172,7 +171,7 @@ jobs:
ARCH=$(uname -m)
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "SHORT_SHA=$SHORT_SHA" >> "$GITHUB_ENV"
echo "ASSET_BASENAME=praktiKST-${VERSION}-${SHORT_SHA}" >> "$GITHUB_ENV"
echo "ASSET_BASENAME=KST4Contest-${VERSION}-${SHORT_SHA}" >> "$GITHUB_ENV"
echo "ARCH=$ARCH" >> "$GITHUB_ENV"
- name: Set up Java 17
@@ -194,7 +193,7 @@ jobs:
mkdir -p dist
jpackage \
--type dmg \
--name praktiKST \
--name KST4Contest \
--input target/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
@@ -217,5 +216,5 @@ jobs:
uses: actions/upload-artifact@v4.3.4
with:
name: macos-dmg-${{ matrix.os }}
path: dist/praktiKST-*-macos-*.dmg
path: dist/KST4Contest-*-macos-*.dmg
retention-days: 14
+19 -19
View File
@@ -88,7 +88,7 @@ jobs:
mkdir -p dist
jpackage \
--type app-image \
--name praktiKST \
--name KST4Contest \
--input target/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
@@ -98,41 +98,41 @@ jobs:
- name: Create AppDir metadata
run: |
rm -rf target/praktiKST.AppDir
cp -a dist/praktiKST target/praktiKST.AppDir
rm -rf target/KST4Contest.AppDir
cp -a dist/KST4Contest target/KST4Contest.AppDir
cat > target/praktiKST.AppDir/AppRun << 'EOF'
cat > target/KST4Contest.AppDir/AppRun << 'EOF'
#!/bin/sh
HERE="$(dirname "$(readlink -f "$0")")"
exec "$HERE/bin/praktiKST" "$@"
exec "$HERE/bin/KST4Contest" "$@"
EOF
chmod +x target/praktiKST.AppDir/AppRun
chmod +x target/KST4Contest.AppDir/AppRun
cat > target/praktiKST.AppDir/praktiKST.desktop << 'EOF'
cat > target/KST4Contest.AppDir/KST4Contest.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=praktiKST
Exec=praktiKST
Icon=praktiKST
Name=KST4Contest
Exec=KST4Contest
Icon=KST4Contest
Categories=Network;HamRadio;
Terminal=false
EOF
if [ -f target/praktiKST.AppDir/lib/praktiKST.png ]; then
cp target/praktiKST.AppDir/lib/praktiKST.png target/praktiKST.AppDir/praktiKST.png
if [ -f target/KST4Contest.AppDir/lib/KST4Contest.png ]; then
cp target/KST4Contest.AppDir/lib/KST4Contest.png target/KST4Contest.AppDir/KST4Contest.png
fi
- name: Build AppImage
run: |
wget -q -O target/appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x target/appimagetool.AppImage
APPIMAGE_EXTRACT_AND_RUN=1 ARCH=x86_64 target/appimagetool.AppImage target/praktiKST.AppDir dist/praktiKST-${{ github.ref_name }}-linux-x86_64.AppImage
APPIMAGE_EXTRACT_AND_RUN=1 ARCH=x86_64 target/appimagetool.AppImage target/KST4Contest.AppDir dist/KST4Contest-${{ github.ref_name }}-linux-x86_64.AppImage
- name: Upload Linux artifact
uses: actions/upload-artifact@v4.3.4
with:
name: linux-appimage
path: dist/praktiKST-${{ github.ref_name }}-linux-x86_64.AppImage
path: dist/KST4Contest-${{ github.ref_name }}-linux-x86_64.AppImage
build-macos-dmg:
name: Build macOS DMG (${{ matrix.os }})
@@ -164,7 +164,7 @@ jobs:
mkdir -p dist
jpackage \
--type dmg \
--name praktiKST \
--name KST4Contest \
--input target/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
@@ -182,13 +182,13 @@ jobs:
if [ -z "$DMG" ]; then
echo "No DMG produced by jpackage" && exit 1
fi
mv "$DMG" "dist/praktiKST-${{ github.ref_name }}-macos-${ARCH}.dmg"
mv "$DMG" "dist/KST4Contest-${{ github.ref_name }}-macos-${ARCH}.dmg"
- name: Upload macOS artifact
uses: actions/upload-artifact@v4.3.4
with:
name: macos-dmg-${{ matrix.os }}
path: dist/praktiKST-${{ github.ref_name }}-macos-*.dmg
path: dist/KST4Contest-${{ github.ref_name }}-macos-*.dmg
build-docs-pdf:
name: Build Documentation PDF
@@ -315,7 +315,7 @@ jobs:
generateReleaseNotes: true
artifacts: >-
release-assets/windows/praktiKST-${{ github.ref_name }}-windows-x64.zip,
release-assets/linux/praktiKST-${{ github.ref_name }}-linux-x86_64.AppImage,
release-assets/macos/praktiKST-${{ github.ref_name }}-macos-*.dmg,
release-assets/linux/KST4Contest-${{ github.ref_name }}-linux-x86_64.AppImage,
release-assets/macos/KST4Contest-${{ github.ref_name }}-macos-*.dmg,
release-assets/docs/KST4Contest-${{ github.ref_name }}-manual-en.pdf,
release-assets/docs/KST4Contest-${{ github.ref_name }}-manual-de.pdf
+29 -2
View File
@@ -46,7 +46,17 @@ Die aktuelle Version kann als AppImage heruntergeladen werden:
**https://github.com/praktimarc/kst4contest/releases/latest**
Der Dateiname hat das Format `praktiKST-v<Versionsnummer>-linux-x86_64.AppImage`.
Der Dateiname hat das Format `KST4Contest-v<Versionsnummer>-linux-x86_64.AppImage`.
### macOS
> ⚠️ **Best-Effort-Support:** macOS-Builds werden als zusätzliche Option bereitgestellt, sind aber **nicht umfassend getestet**. Wir bauen und veröffentlichen macOS-Binaries mit jedem Release, können allerdings nicht alle Szenarien unter macOS testen. Bei Problemen freuen wir uns über eine Rückmeldung – wir versuchen unser Bestes, können aber nicht den gleichen Support-Umfang wie für Windows und Linux garantieren.
Die aktuelle Version kann als DMG-Disk-Image heruntergeladen werden (für Apple-Silicon- und Intel-Macs verfügbar):
**https://github.com/praktimarc/kst4contest/releases/latest**
Der Dateiname hat das Format `KST4Contest-v<Versionsnummer>-macos-<Architektur>.dmg`, wobei `<Architektur>` entweder `arm64` (Apple Silicon) oder `x86_64` (Intel) ist.
---
@@ -64,11 +74,22 @@ Die Einstellungen werden unter `%USERPROFILE%\.praktikst\preferences.xml` gespei
### Linux
1. AppImage herunterladen.
2. AppImage in gewünschten Ordner entpacken.
3. AppImage ausführbar machen (geht im Terminal mit `chmod +x praktiKST-v<Versionsnummer>-linux-x86_64.AppImage`)
3. AppImage ausführbar machen (geht im Terminal mit `chmod +x KST4Contest-v<Versionsnummer>-linux-x86_64.AppImage`)
4. AppImage ausführen.
Die Einstellungen werden unter `~/.praktikst/preferences.xml` gespeichert.
### macOS
1. DMG-Datei für die eigene Architektur herunterladen (Apple Silicon oder Intel).
2. DMG-Datei öffnen.
3. `KST4Contest.app` in den **Programme**-Ordner ziehen.
4. Beim ersten Start zeigt macOS ggf. eine Warnung, da die App nicht notarisiert ist. Zum Öffnen:
- Rechtsklick (oder Ctrl-Klick) auf `KST4Contest.app` im Finder → **Öffnen** wählen.
- Alternativ: **Systemeinstellungen → Datenschutz & Sicherheit** → **Trotzdem öffnen** klicken.
5. KST4Contest aus dem Programme-Ordner oder dem Launchpad starten.
Die Einstellungen werden unter `~/.praktikst/preferences.xml` gespeichert.
---
## Update
@@ -98,6 +119,12 @@ Derzeit folgendermaßen:
2. neues AppImage ausführbar makieren
3. (optional) altes AppImage löschen.
#### macOS
1. Neue DMG-Datei herunterladen.
2. DMG öffnen.
3. Die neue `KST4Contest.app` in den **Programme**-Ordner ziehen und die alte Version ersetzen.
---
+18 -7
View File
@@ -83,20 +83,26 @@ Für den integrierten DX-Cluster-Server: N1MM+ als DX-Cluster-Client konfigurier
### Win-Test
Win-Test wird mit einem dedizierten UDP-Netzwerk-Listener unterstützt, der das native Win-Test Netzwerkprotokoll versteht.
Win-Test wird mit einem dedizierten UDP-Netzwerk-Listener unterstützt, der das native Win-Test Netzwerkprotokoll versteht.
**Vorteile der Win-Test Integration:**
- Automatische QSO-Synchronisation zur Markierung gearbeiteter Stationen.
- **Sked-Übergabe (ADDSKED):** Über den Button "Create sked" im Stationsinfo-Panel wird nicht nur in KST4Contest ein Sked angelegt, sondern dieses auch *direkt per UDP an das Win-Test Netzwerk als ADDSKED-Paket gesendet*.
- **Sked-Übergabe (ADDSKED):** Über den Button "Create sked" im Stationsinfo-Panel wird nicht nur in KST4Contest ein Sked angelegt, sondern dieser auch *direkt per UDP an das Win-Test Netzwerk als ADDSKED-Paket gesendet* – automatisch, sobald der Listener aktiv ist.
- Es kann zwischen den Sked-Modi "AUTO", "SSB" oder "CW" gewählt werden.
- **Automatische QRG-Auflösung für SKEDs:** KST4Contest wählt die Sked-Frequenz intelligent:
1. Hat die Gegenstation in einer Chat-Nachricht ihre QRG genannt, wird diese verwendet.
2. Sonst wird die eigene aktuelle QRG verwendet (aus Win-Test STATUS oder manueller Eingabe).
**Notwendige Einstellungen in KST4Contest:**
- `UDP-Port for Win-Test listener` (Standard: 9871).
**Einstellungen im Reiter „Log-Synchronisation":**
- `Receive Win-Test network based UDP log messages` aktivieren.
- `Win-Test sked transmission (push via ADDSKED to Win-Test network)` aktivieren.
- `UDP-Port for Win-Test listener` (Standard: 9871).
- `KST station name in Win-Test network (src of SKED packets)`: Legt fest, unter welchem Stationsnamen KST4Contest im WT-Netzwerk auftritt (z.B. "KST").
- `Win-Test station name filter`: Wenn hier ein Name eingetragen wird (z.B. "STN1"), werden nur QSOs von dieser bestimmten Win-Test Instanz verarbeitet. Leer lassen, um alle zu akzeptieren.
- `Win-Test network broadcast address`: Wird idR automatisch erkannt und ist erforderlich, um die Sked-Pakete ins Netzwerk zu senden.
- `Win-Test network broadcast address`: Wird i.d.R. automatisch erkannt; erforderlich für das Senden von Sked-Paketen.
**Einstellungen im Reiter „TRX-Synchronisation":**
- `Win-Test STATUS QRG Sync`: Wenn aktiviert, übernimmt KST4Contest die aktuelle Transceiverfrequenz aus dem Win-Test STATUS-Paket als eigene QRG (MYQRG).
- `Use pass frequency from Win-Test STATUS`: Statt der eigenen TRX-QRG wird die im STATUS-Paket enthaltene Pass-Frequenz als MYQRG verwendet (für Multi-Op-Setups, bei denen mit einer Pass-QRG gearbeitet wird).
- `Win-Test station name filter`: Wird hier ein Name eingetragen (z.B. "STN1"), verarbeitet KST4Contest nur Pakete dieser Win-Test-Instanz. Leer lassen, um alle zu akzeptieren.
**Einstellungen in Win-Test:**
- Das Netzwerk in Win-Test muss aktiv sein.
@@ -112,6 +118,11 @@ 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.
**Quellen für die eigene QRG (MYQRG):**
- UCXLog, N1MM+, DXLog.net, QARTest via UDP-Port 12060
- Win-Test STATUS-Paket (optional, konfigurierbar im Reiter „TRX-Synchronisation" unter „Win-Test STATUS QRG Sync")
- Manuelle Eingabe im QRG-Feld
> **Hinweis für Multi-Setup**: Bei zwei Logprogrammen an zwei Computern sollte nur **eines** die Frequenzpakete senden. KST4Contest kann nicht zwischen den Quellen unterscheiden und verarbeitet alle eingehenden Pakete.
---
+29 -2
View File
@@ -46,7 +46,17 @@ The latest version can be downloaded as an AppImage:
**https://github.com/praktimarc/kst4contest/releases/latest**
The filename has the format `praktiKST-v<version_number>-linux-x86_64.AppImage`.
The filename has the format `KST4Contest-v<version_number>-linux-x86_64.AppImage`.
### macOS
> ⚠️ **Best-Effort Support:** macOS builds are provided as a convenience but are **not fully tested**. We build and release macOS binaries with every release, but we cannot test every scenario on macOS. If you encounter issues, please report them – we will do our best to address them, but cannot guarantee the same level of support as for Windows and Linux.
The latest version can be downloaded as a DMG disk image (available for both Apple Silicon and Intel Macs):
**https://github.com/praktimarc/kst4contest/releases/latest**
The filename has the format `KST4Contest-v<version_number>-macos-<arch>.dmg`, where `<arch>` is `arm64` (Apple Silicon) or `x86_64` (Intel).
---
@@ -64,11 +74,22 @@ Settings are stored at `%USERPROFILE%\.praktikst\preferences.xml`.
### Linux
1. Download the AppImage.
2. Unzip the AppImage into a folder of your choice.
3. Make the AppImage executable (in the terminal with `chmod +x praktiKST-v<version_number>-linux-x86_64.AppImage`)
3. Make the AppImage executable (in the terminal with `chmod +x KST4Contest-v<version_number>-linux-x86_64.AppImage`)
4. Run the AppImage.
Settings are stored at `~/.praktikst/preferences.xml`.
### macOS
1. Download the DMG file for your architecture (Apple Silicon or Intel).
2. Open the DMG file.
3. Drag `KST4Contest.app` into your **Applications** folder.
4. On first launch, macOS may show a warning because the app is not notarised. To open it:
- Right-click (or Control-click) on `KST4Contest.app` in Finder and choose **Open**.
- Alternatively, go to **System Settings → Privacy & Security** and click **Open Anyway**.
5. Run KST4Contest from your Applications folder or Launchpad.
Settings are stored at `~/.praktikst/preferences.xml`.
---
## Updating
@@ -98,6 +119,12 @@ Currently as follows:
2. Mark the new AppImage as executable
3. (optional) Delete the old AppImage.
#### macOS
1. Download the new DMG file.
2. Open the DMG.
3. Drag the new `KST4Contest.app` into your **Applications** folder, replacing the old version.
---
+18 -7
View File
@@ -87,16 +87,22 @@ Win-Test is supported with a dedicated UDP network listener that understands the
**Advantages of Win-Test Integration:**
- Automatic QSO synchronization to mark worked stations.
- **Sked Handover (ADDSKED):** Using the "Create sked" button in the station info panel not only creates a sked in KST4Contest but also *sends it directly via UDP to the Win-Test network as an ADDSKED packet*.
- **Sked Handover (ADDSKED):** Using the "Create sked" button in the station info panel not only creates a sked in KST4Contest but also *sends it directly via UDP to the Win-Test network as an ADDSKED packet* – automatically, as soon as the listener is active. No separate toggle is needed.
- You can choose between "AUTO", "SSB", or "CW" sked modes.
- **Automatic QRG resolution for SKEDs:** KST4Contest selects the sked frequency intelligently:
1. If the other station mentioned their QRG in a recent chat message, that frequency is used.
2. Otherwise, your own current QRG is used (from Win-Test STATUS or manual entry).
**Required Settings in KST4Contest:**
- `UDP-Port for Win-Test listener` (Default: 9871).
**Settings in the "Log Synchronisation" tab:**
- Enable `Receive Win-Test network based UDP log messages`.
- Enable `Win-Test sked transmission (push via ADDSKED to Win-Test network)`.
- `KST station name in Win-Test network (src of SKED packets)`: Defines the station name KST4Contest uses in the WT network (e.g., "KST").
- `Win-Test station name filter`: If a name is entered here (e.g., "STN1"), only QSOs from that specific Win-Test instance will be processed. Leave empty to accept all.
- `Win-Test network broadcast address`: Is usually detected automatically and is required to send sked packets to the network.
- `UDP-Port for Win-Test listener` (default: 9871).
- `KST station name in Win-Test network (src of SKED packets)`: Defines the station name KST4Contest uses in the WT network (e.g. "KST").
- `Win-Test network broadcast address`: Usually detected automatically; required to send sked packets to the network.
**Settings in the "TRX Synchronisation" tab:**
- `Win-Test STATUS QRG Sync`: When enabled, KST4Contest takes the current transceiver frequency from the Win-Test STATUS packet and uses it as your own QRG (MYQRG).
- `Use pass frequency from Win-Test STATUS`: Instead of the main TRX frequency, the pass frequency contained in the STATUS packet is used as MYQRG (useful for multi-op setups that operate with a dedicated pass QRG).
- `Win-Test station name filter`: If a name is entered here (e.g. "STN1"), KST4Contest only processes packets from that specific Win-Test instance. Leave empty to accept all.
**Settings in Win-Test:**
- The network in Win-Test must be active.
@@ -112,6 +118,11 @@ 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.
**Sources for your own QRG (MYQRG):**
- UCXLog, N1MM+, DXLog.net, QARTest via UDP port 12060
- Win-Test STATUS packet (optional, configurable in the "TRX Synchronisation" tab under "Win-Test STATUS QRG Sync")
- Manual entry in the QRG field
> **Note for multi-setup**: With two logging programs on two computers, only **one** should send frequency packets. KST4Contest cannot distinguish between sources and processes all incoming packets.
---
@@ -810,6 +810,24 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
private final Map<String, ChatCategory> lastInboundCategoryByCallSignRaw =
new java.util.concurrent.ConcurrentHashMap<>();
/** Tracks the last time WE sent a message containing a QRG to a specific callsign (UPPERCASE).
* Compared against knownActiveBands.timestampEpoch to decide whose QRG to use in a SKED. */
private final Map<String, Long> lastSentQRGToCallsign =
new java.util.concurrent.ConcurrentHashMap<>();
/** Call this whenever we send a PM to {@code receiverCallsign} that contains our QRG. */
public void recordOutboundQRG(String receiverCallsign) {
if (receiverCallsign == null) return;
lastSentQRGToCallsign.put(receiverCallsign.trim().toUpperCase(), System.currentTimeMillis());
System.out.println("[ChatController] Recorded outbound QRG to: " + receiverCallsign);
}
/** Returns epoch-ms of when we last sent our QRG to this callsign, or 0 if never. */
public long getLastSentQRGTimestamp(String callsign) {
if (callsign == null) return 0L;
return lastSentQRGToCallsign.getOrDefault(callsign.trim().toUpperCase(), 0L);
}
private final ScoreService scoreService = new ScoreService(this, new PriorityCalculator(), 15);
private ScheduledExecutorService scoreScheduler;
private final StationMetricsService stationMetricsService = new StationMetricsService();
@@ -827,8 +845,7 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
});
// Push sked to Win-Test via UDP if enabled
if (chatPreferences.isLogsynch_wintestNetworkSkedPushEnabled()
&& chatPreferences.isLogsynch_wintestNetworkListenerEnabled()) {
if (chatPreferences.isLogsynch_wintestNetworkListenerEnabled()) {
pushSkedToWinTest(sked);
}
}
@@ -847,16 +864,69 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
WinTestSkedSender sender = new WinTestSkedSender(stationName, broadcastAddr, port, this);
// Get current frequency from QRG property (set by Win-Test STATUS or user)
double freqKHz = 144300.0; // fallback default
try {
String qrgStr = chatPreferences.getMYQRGFirstCat().get();
if (qrgStr != null && !qrgStr.isBlank()) {
// QRG is in display format like "144.300.00" – strip dots → "14430000" → / 100 → 144300.0 kHz
String cleaned = qrgStr.trim().replace(".", "");
freqKHz = Double.parseDouble(cleaned) / 100.0;
// Frequency resolution:
// Compare WHO sent a QRG most recently in the PM conversation:
// - OM sent their QRG last → use OM's Last Known QRG (ChatMember.frequency)
// - WE sent our QRG last → use our own Win-Test QRG (MYQRG)
// Fallback chain if no timestamps exist: OM's Last Known QRG → hardcoded default
double freqKHz = -1.0;
final long SKED_FREQ_MAX_AGE_MS = 60 * 60 * 1000L; // 60 minutes
ChatMember targetMember = resolveSkedTargetMember(sked.getTargetCallsign());
// Collect timestamps: when did the OM last mention their QRG? When did WE last send ours?
long omLastQRGTimestamp = 0L;
double omLastQRGMhz = 0.0;
if (targetMember != null && sked.getBand() != null) {
ChatMember.ActiveFrequencyInfo fi = targetMember.getKnownActiveBands().get(sked.getBand());
if (fi != null && fi.frequency > 0
&& (System.currentTimeMillis() - fi.timestampEpoch) <= SKED_FREQ_MAX_AGE_MS) {
omLastQRGTimestamp = fi.timestampEpoch;
omLastQRGMhz = fi.frequency;
}
} catch (NumberFormatException ignored) { }
}
long ourLastQRGTimestamp = getLastSentQRGTimestamp(sked.getTargetCallsign());
// Decision: who was more recent?
if (omLastQRGTimestamp > 0 && omLastQRGTimestamp >= ourLastQRGTimestamp) {
// OM mentioned their QRG MORE RECENTLY (or at same time) → use their QRG
freqKHz = omLastQRGMhz * 1000.0;
System.out.println("[ChatController] SKED freq: OM sent last → "
+ omLastQRGMhz + " MHz → " + freqKHz + " kHz");
} else if (ourLastQRGTimestamp > 0) {
// WE sent our QRG more recently → use our Win-Test QRG
try {
String qrgStr = chatPreferences.getMYQRGFirstCat().get();
if (qrgStr != null && !qrgStr.isBlank()) {
String cleaned = qrgStr.trim().replace(".", "");
double parsed = Double.parseDouble(cleaned) / 100.0;
if (parsed > 50000) {
freqKHz = parsed;
System.out.println("[ChatController] SKED freq: WE sent last → "
+ freqKHz + " kHz (raw: " + qrgStr + ")");
}
}
} catch (NumberFormatException ignored) { }
}
// Fallback A: OM's Last Known QRG from KST field (if no PM QRG exchange found at all)
if (freqKHz < 0 && targetMember != null) {
try {
String memberQrg = targetMember.getFrequency().get();
if (memberQrg != null && !memberQrg.isBlank()) {
double mhz = Double.parseDouble(memberQrg.trim());
freqKHz = mhz * 1000.0;
System.out.println("[ChatController] SKED freq: fallback Last Known QRG → "
+ mhz + " MHz → " + freqKHz + " kHz");
}
} catch (NumberFormatException ignored) { }
}
// Fallback B: hardcoded default
if (freqKHz < 0) {
freqKHz = 144300.0;
}
// Build notes string with target locator/azimuth info like reference: [JO02OB - 279°]
String targetLocator = resolveSkedTargetLocator(sked.getTargetCallsign());
@@ -883,6 +953,22 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
}, "WinTestSkedPush").start();
}
private ChatMember resolveSkedTargetMember(String targetCallsignRaw) {
if (targetCallsignRaw == null || targetCallsignRaw.isBlank()) {
return null;
}
String normalizedTargetCall = normalizeCallRaw(targetCallsignRaw);
synchronized (getLst_chatMemberList()) {
for (ChatMember member : getLst_chatMemberList()) {
if (member == null || member.getCallSignRaw() == null) continue;
if (normalizeCallRaw(member.getCallSignRaw()).equals(normalizedTargetCall)) {
return member;
}
}
}
return null;
}
private String resolveSkedTargetLocator(String targetCallsignRaw) {
if (targetCallsignRaw == null || targetCallsignRaw.isBlank()) {
return null;
@@ -962,6 +962,13 @@ public class MessageBusManagementThread extends Thread {
.setMessageText("(>" + newMessageArrived.getReceiver().getCallSign() + ")" + originalMessage);
this.client.getLst_globalChatMessageList().add(0,newMessageArrived);
// If our message contained a frequency (e.g. "QRG is: 144.375"), record that
// WE sent our QRG to this OM – used by SKED frequency resolution.
if (originalMessage != null && newMessageArrived.getReceiver() != null
&& originalMessage.matches(".*\\b\\d{3,5}[.,]\\d{1,3}.*")) {
this.client.recordOutboundQRG(newMessageArrived.getReceiver().getCallSign());
}
// if you sent the message to another station, it will be sorted in to
// the "to me message list" with modified messagetext, added rxers callsign
@@ -1,5 +1,6 @@
package kst4contest.controller;
import javafx.application.Platform;
import kst4contest.ApplicationConstants;
import kst4contest.model.ChatMember;
import kst4contest.model.ThreadStateMessage;
@@ -75,9 +76,10 @@ public class ReadUDPByWintestThread extends Thread {
socket = new DatagramSocket(null); //first init with null, then make ready for reuse
socket.setReuseAddress(true);
// socket = new DatagramSocket(PORT);
socket.bind(new InetSocketAddress(client.getChatPreferences().getLogsynch_wintestNetworkPort()));
int boundPort = client.getChatPreferences().getLogsynch_wintestNetworkPort();
socket.bind(new InetSocketAddress(boundPort));
socket.setSoTimeout(3000);
System.out.println("[WinTest UDP listener] started at port: " + PORT);
System.out.println("[WinTest UDP listener] started at port: " + boundPort);
} catch (SocketException e) {
e.printStackTrace();
return;
@@ -224,9 +226,43 @@ public class ReadUDPByWintestThread extends Thread {
} else {
formattedQRG = String.format(Locale.US, "%.1f", freqFloat); // fallback
}
this.client.getChatPreferences().getMYQRGFirstCat().set(formattedQRG);
// Parse pass frequency from parts[11] if available (WT STATUS format)
String formattedPassQRG = null;
if (parts.size() > 11) {
try {
String passFreqRaw = parts.get(11);
double passFreqFloat = Integer.parseInt(passFreqRaw) / 10.0;
if (passFreqFloat > 100) { // Must be a valid radio frequency (> 100 kHz), protects against parsing boolean flag tokens
long passFreqHzTimes100 = Math.round(passFreqFloat * 100.0);
String passHzStr = String.valueOf(passFreqHzTimes100);
if (passHzStr.length() == 8) {
formattedPassQRG = String.format("%s.%s.%s", passHzStr.substring(0, 3), passHzStr.substring(3, 6), passHzStr.substring(6, 8));
} else if (passHzStr.length() == 9) {
formattedPassQRG = String.format("%s.%s.%s", passHzStr.substring(0, 4), passHzStr.substring(4, 7), passHzStr.substring(7, 9));
} else if (passHzStr.length() == 7) {
formattedPassQRG = String.format("%s.%s.%s", passHzStr.substring(0, 2), passHzStr.substring(2, 5), passHzStr.substring(5, 7));
} else if (passHzStr.length() == 6) {
formattedPassQRG = String.format("%s.%s.%s", passHzStr.substring(0, 1), passHzStr.substring(1, 4), passHzStr.substring(4, 6));
} else {
formattedPassQRG = String.format(Locale.US, "%.1f", passFreqFloat);
}
}
} catch (Exception ignored) {
// parts[11] not a valid frequency, leave formattedPassQRG as null
}
}
System.out.println("[WinTest STATUS] stn=" + stn + ", mode=" + mode + ", qrg=" + formattedQRG);
if (this.client.getChatPreferences().isLogsynch_wintestQrgSyncEnabled()) {
final String qrgToSet = (this.client.getChatPreferences().isLogsynch_wintestUsePassQrg() && formattedPassQRG != null)
? formattedPassQRG
: formattedQRG;
// JavaFX StringProperty must be updated on the FX Application Thread
Platform.runLater(() -> this.client.getChatPreferences().getMYQRGFirstCat().set(qrgToSet));
}
System.out.println("[WinTest STATUS] stn=" + stn + ", mode=" + mode + ", qrg=" + formattedQRG
+ (formattedPassQRG != null ? ", passQrg=" + formattedPassQRG : "")
+ ", syncActive=" + this.client.getChatPreferences().isLogsynch_wintestQrgSyncEnabled());
} catch (Exception e) {
System.out.println("[WinTest] STATUS parsing error: " + e.getMessage());
}
@@ -204,6 +204,8 @@ public class ChatPreferences {
String logsynch_wintestNetworkBroadcastAddress = "255.255.255.255"; // UDP broadcast address for sending to Win-Test
boolean logsynch_wintestNetworkSkedPushEnabled = false; // push SKEDs to Win-Test via UDP
String logsynch_wintestSkedMode = "SSB"; // CW, SSB or AUTO
boolean logsynch_wintestQrgSyncEnabled = true; // sync QRG from Win-Test STATUS packet
boolean logsynch_wintestUsePassQrg = false; // use pass frequency instead of main QRG from STATUS packet
@@ -481,6 +483,22 @@ public class ChatPreferences {
this.logsynch_wintestSkedMode = logsynch_wintestSkedMode;
}
public boolean isLogsynch_wintestQrgSyncEnabled() {
return logsynch_wintestQrgSyncEnabled;
}
public void setLogsynch_wintestQrgSyncEnabled(boolean logsynch_wintestQrgSyncEnabled) {
this.logsynch_wintestQrgSyncEnabled = logsynch_wintestQrgSyncEnabled;
}
public boolean isLogsynch_wintestUsePassQrg() {
return logsynch_wintestUsePassQrg;
}
public void setLogsynch_wintestUsePassQrg(boolean logsynch_wintestUsePassQrg) {
this.logsynch_wintestUsePassQrg = logsynch_wintestUsePassQrg;
}
public String getStn_loginLocatorSecondCat() {
return stn_loginLocatorSecondCat;
}
@@ -1338,6 +1356,14 @@ public class ChatPreferences {
logsynch_wintestSkedMode.setTextContent(this.logsynch_wintestSkedMode);
logsynch.appendChild(logsynch_wintestSkedMode);
Element logsynch_wintestQrgSyncEnabled = doc.createElement("logsynch_wintestQrgSyncEnabled");
logsynch_wintestQrgSyncEnabled.setTextContent(this.logsynch_wintestQrgSyncEnabled + "");
logsynch.appendChild(logsynch_wintestQrgSyncEnabled);
Element logsynch_wintestUsePassQrg = doc.createElement("logsynch_wintestUsePassQrg");
logsynch_wintestUsePassQrg.setTextContent(this.logsynch_wintestUsePassQrg + "");
logsynch.appendChild(logsynch_wintestUsePassQrg);
/**
* trxSynchUCX
@@ -1912,6 +1938,16 @@ public class ChatPreferences {
logsynch_wintestSkedMode,
"logsynch_wintestSkedMode");
logsynch_wintestQrgSyncEnabled = getBoolean(
logsynchEl,
logsynch_wintestQrgSyncEnabled,
"logsynch_wintestQrgSyncEnabled");
logsynch_wintestUsePassQrg = getBoolean(
logsynchEl,
logsynch_wintestUsePassQrg,
"logsynch_wintestUsePassQrg");
System.out.println(
"[ChatPreferences, info]: file based worked-call interpreter: " + logsynch_fileBasedWkdCallInterpreterEnabled);
System.out.println(
@@ -3582,7 +3582,57 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
Menu fileMenu = new Menu("File");
// create menuitems
// build "Connect to <configured chat>" label from saved preferences
ChatCategory mainCat = chatcontroller.getChatPreferences().getLoginChatCategoryMain();
String connectLabel = "Connect to " + mainCat.getChatCategoryName(mainCat.getCategoryNumber());
if (chatcontroller.getChatPreferences().isLoginToSecondChatEnabled()) {
ChatCategory secCat = chatcontroller.getChatPreferences().getLoginChatCategorySecond();
if (secCat != null) {
connectLabel += " & " + secCat.getChatCategoryName(secCat.getCategoryNumber());
}
}
menuItemFileConnect = new MenuItem(connectLabel);
menuItemFileConnect.setDisable(false);
if (chatcontroller.isConnectedAndLoggedIn() || chatcontroller.isConnectedAndNOTLoggedIn()) {
menuItemFileConnect.setDisable(true);
}
menuItemFileConnect.setOnAction(event -> {
System.out.println("[Info] File menu: Connect clicked, using saved preferences");
String call = chatcontroller.getChatPreferences().getStn_loginCallSign();
String pass = chatcontroller.getChatPreferences().getStn_loginPassword();
if (call == null || call.isBlank() || pass == null || pass.isBlank()) {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Cannot connect");
alert.setHeaderText("Login credentials missing");
alert.setContentText("Please configure your callsign and password in Settings first.");
alert.show();
return;
}
try {
chatcontroller.execute();
menuItemFileConnect.setDisable(true);
menuItemFileDisconnect.setDisable(false);
menuItemOptionsAwayBack.setDisable(false);
menuItemOptionsSetFrequencyAsName.setDisable(false);
chatcontroller.setConnectedAndLoggedIn(true);
chatcontroller.setDisconnected(false);
} catch (InterruptedException | IOException e) {
e.printStackTrace();
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Connection failed");
alert.setContentText("Could not connect: " + e.getMessage());
alert.show();
}
});
menuItemFileDisconnect = new MenuItem("Disconnect");
menuItemFileDisconnect.setDisable(true);
@@ -3595,6 +3645,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
public void handle(ActionEvent event) {
chatcontroller.disconnect(ApplicationConstants.DISCSTRING_DISCONNECTONLY);
menuItemFileDisconnect.setDisable(true);
menuItemFileConnect.setDisable(false);
}
});
@@ -3607,6 +3658,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
});
// add menu items to menu
fileMenu.getItems().add(menuItemFileConnect);
fileMenu.getItems().add(menuItemFileDisconnect);
fileMenu.getItems().add(m10);
@@ -4010,6 +4062,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
Scene clusterAndQSOMonScene;
Scene settingsScene;
MenuItem menuItemFileConnect;
MenuItem menuItemFileDisconnect;
MenuItem menuItemOptionsAwayBack;
@@ -4170,10 +4223,15 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
timer_updatePrivatemessageTable.purge();
timer_updatePrivatemessageTable.cancel();
chatcontroller.disconnect("CLOSEALL");
try {
chatcontroller.disconnect("CLOSEALL");
} catch (Exception e) {
System.out.println("[Main.java, Warning:] Exception during disconnect: " + e.getMessage());
}
// Platform.exit();
System.exit(0);
}
private Queue<Media> musicList = new LinkedList<Media>();
@@ -6273,11 +6331,14 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
}
});
boolean isSecondChatEnabled = this.chatcontroller.getChatPreferences().isLoginToSecondChatEnabled();
Label lblNameSecondCat = new Label("Name in Chat 2:");
lblNameSecondCat.setVisible(false);
lblNameSecondCat.setVisible(isSecondChatEnabled);
lblNameSecondCat.setDisable(!isSecondChatEnabled);
TextField txtFldNameInChatSecondCat = new TextField(this.chatcontroller.getChatPreferences().getStn_loginNameSecondCat());
txtFldNameInChatSecondCat.setFocusTraversable(false);
txtFldNameInChatSecondCat.setVisible(false);
txtFldNameInChatSecondCat.setVisible(isSecondChatEnabled);
txtFldNameInChatSecondCat.setDisable(!isSecondChatEnabled);
txtFldNameInChatSecondCat.textProperty().addListener(new ChangeListener<String>() {
@@ -6397,11 +6458,12 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
CheckBox station_chkBxEnableSecondChat = new CheckBox("2nd Chat: ");
station_chkBxEnableSecondChat.setSelected(chatcontroller.getChatPreferences().isLoginToSecondChatEnabled());
boolean isSecondChatEnabledForCheckbox = chatcontroller.getChatPreferences().isLoginToSecondChatEnabled();
station_chkBxEnableSecondChat.setSelected(isSecondChatEnabledForCheckbox);
stn_choiceBxChatChategorySecond.setDisable(true);
stn_choiceBxChatChategorySecond.setDisable(!isSecondChatEnabledForCheckbox);
station_chkBxEnableSecondChat.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
@@ -6431,12 +6493,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
}
});
if (chatcontroller.getChatPreferences().isLoginToSecondChatEnabled()) {
stn_choiceBxChatChategorySecond.setVisible(chatcontroller.getChatPreferences().isLoginToSecondChatEnabled());
stn_choiceBxChatChategorySecond.setDisable(!chatcontroller.getChatPreferences().isLoginToSecondChatEnabled());
txtFldNameInChatSecondCat.setVisible(chatcontroller.getChatPreferences().isLoginToSecondChatEnabled());
}
TextField txtFldstn_antennaBeamWidthDeg = new TextField(this.chatcontroller.getChatPreferences().getStn_antennaBeamWidthDeg() + "");
txtFldstn_antennaBeamWidthDeg.setFocusTraversable(false);
@@ -6667,7 +6724,6 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
grdPnlStation_bands.add(settings_chkbx_QRV3400, 1, 2);
grdPnlStation_bands.add(settings_chkbx_QRV5600, 2, 2);
grdPnlStation_bands.add(settings_chkbx_QRV10G, 0, 3);
grdPnlStation_bands.setMaxWidth(555.0);
grdPnlStation_bands.setStyle(" -fx-border-color: lightgray;\n" +
" -fx-vgap: 5;\n" +
@@ -6882,15 +6938,32 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
grdPnlLog.add(lblUDPByWintest, 0, 8);
grdPnlLog.add(txtFldUDPPortforWintest, 1, 8);
// --- Win-Test SKED push settings ---
Label lblEnableSkedPush = new Label("Push SKEDs to Win-Test via UDP (ADDSKED)");
CheckBox chkBxEnableSkedPush = new CheckBox();
chkBxEnableSkedPush.setSelected(
this.chatcontroller.getChatPreferences().isLogsynch_wintestNetworkSkedPushEnabled()
// --- QRG sync from Win-Test STATUS ---
Label lblWtQrgSync = new Label("Win-Test STATUS QRG Sync (updates own QRG from Win-Test transceiver frequency)");
CheckBox chkBxWtQrgSync = new CheckBox();
chkBxWtQrgSync.setSelected(
this.chatcontroller.getChatPreferences().isLogsynch_wintestQrgSyncEnabled()
);
chkBxEnableSkedPush.selectedProperty().addListener((obs, oldVal, newVal) -> {
chatcontroller.getChatPreferences().setLogsynch_wintestNetworkSkedPushEnabled(newVal);
System.out.println("[Main.java, Info]: Win-Test SKED push enabled: " + newVal);
chkBxWtQrgSync.selectedProperty().addListener((obs, oldVal, newVal) -> {
chatcontroller.getChatPreferences().setLogsynch_wintestQrgSyncEnabled(newVal);
System.out.println("[Main.java, Info]: Win-Test QRG sync enabled: " + newVal);
boolean anyActive = chatcontroller.getChatPreferences().isTrxSynch_ucxLogUDPListenerEnabled() || newVal;
if (!anyActive) {
txt_ownqrgMainCategory.textProperty().unbind();
txt_ownqrgMainCategory.setTooltip(new Tooltip("Your cq qrg will be updated by hand (watch prefs!)"));
} else {
txt_ownqrgMainCategory.textProperty().bind(chatcontroller.getChatPreferences().getMYQRGFirstCat());
txt_ownqrgMainCategory.setTooltip(new Tooltip("Your cq qrg will be updated by the log program (watch prefs!)"));
}
});
Label lblWtUsePassQrg = new Label("Use pass frequency from Win-Test STATUS (instead of own QRG)");
CheckBox chkBxWtUsePassQrg = new CheckBox();
chkBxWtUsePassQrg.setSelected(
this.chatcontroller.getChatPreferences().isLogsynch_wintestUsePassQrg()
);
chkBxWtUsePassQrg.selectedProperty().addListener((obs, oldVal, newVal) -> {
chatcontroller.getChatPreferences().setLogsynch_wintestUsePassQrg(newVal);
System.out.println("[Main.java, Info]: Win-Test use pass QRG: " + newVal);
});
Label lblWtStationName = new Label("KST station name in Win-Test network (src of SKED packets)");
@@ -6935,13 +7008,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
}
});
grdPnlLog.add(lblEnableSkedPush, 0, 9);
grdPnlLog.add(chkBxEnableSkedPush, 1, 9);
grdPnlLog.add(lblWtStationName, 0, 11);
grdPnlLog.add(txtFldWtStationName, 1, 11);
grdPnlLog.add(lblWtStationFilter, 0, 12);
grdPnlLog.add(txtFldWtStationFilter, 1, 12);
grdPnlLog.add(lblWtStationName, 0, 9);
grdPnlLog.add(txtFldWtStationName, 1, 9);
// Auto-detect subnet broadcast if preference is still the default
String currentBroadcast = this.chatcontroller.getChatPreferences().getLogsynch_wintestNetworkBroadcastAddress();
@@ -6959,8 +7027,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
// Re-read (may have been auto-detected)
txtFldWtBroadcastAddr.setText(this.chatcontroller.getChatPreferences().getLogsynch_wintestNetworkBroadcastAddress());
grdPnlLog.add(lblWtBroadcastAddr, 0, 13);
grdPnlLog.add(txtFldWtBroadcastAddr, 1, 13);
grdPnlLog.add(lblWtBroadcastAddr, 0, 10);
grdPnlLog.add(txtFldWtBroadcastAddr, 1, 10);
VBox vbxLog = new VBox();
vbxLog.setPadding(new Insets(10, 10, 10, 10));
@@ -6995,51 +7063,45 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
chkBxEnableTRXMsgbyUCX.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// chk2.setSelected(!newValue);
if (!newValue) {
chatcontroller.getChatPreferences()
.setTrxSynch_ucxLogUDPListenerEnabled(chkBxEnableTRXMsgbyUCX.isSelected());
chatcontroller.getChatPreferences().setTrxSynch_ucxLogUDPListenerEnabled(newValue);
boolean anyActive = newValue || chatcontroller.getChatPreferences().isLogsynch_wintestQrgSyncEnabled();
if (!anyActive) {
txt_ownqrgMainCategory.textProperty().unbind();
txt_ownqrgMainCategory.setTooltip(new Tooltip("Your cq qrg will be updated by hand (watch prefs!)"));
System.out.println("[Main.java, Info]: MYQRG will be changed only by User input");
System.out.println("[Main.java, Info]: setted the trx-frequency updated by ucxlog to: "
+ chatcontroller.getChatPreferences().isTrxSynch_ucxLogUDPListenerEnabled());
} else {
chatcontroller.getChatPreferences()
.setTrxSynch_ucxLogUDPListenerEnabled(chkBxEnableTRXMsgbyUCX.isSelected());
txt_ownqrgMainCategory.textProperty().bind(chatcontroller.getChatPreferences().getMYQRGFirstCat());
txt_ownqrgMainCategory.setTooltip(new Tooltip("Your cq qrg will be updated by the log program (watch prefs!)"));
System.out.println("[Main.java, Info]: setted the trx-frequency updated by ucxlog to: "
+ chatcontroller.getChatPreferences().isTrxSynch_ucxLogUDPListenerEnabled());
}
}
});
// Thats the default behaviour of the myqrg textfield
if (this.chatcontroller.getChatPreferences().isTrxSynch_ucxLogUDPListenerEnabled()) {
// Unconditionally add listener to manually sync the textfield input to the button
// (this listener also fires correctly when the value is updated by the binding)
txt_ownqrgMainCategory.textProperty().addListener((observable, oldValue, newValue) -> {
MYQRGButton.textProperty().set(newValue);
});
// That's the default behaviour of the myqrg textfield
if (this.chatcontroller.getChatPreferences().isTrxSynch_ucxLogUDPListenerEnabled() || this.chatcontroller.getChatPreferences().isLogsynch_wintestQrgSyncEnabled()) {
txt_ownqrgMainCategory.setTooltip(new Tooltip("Your cq qrg will be updated by the log program (watch prefs!)"));
txt_ownqrgMainCategory.textProperty().bind(this.chatcontroller.getChatPreferences().getMYQRGFirstCat());// TODO: Bind darf nur
// gemacht werden, wenn
// ucxlog-Frequenznachrichten
// ausgewerttet werden!
// System.out.println("[Main.java, Info]: MYQRG will be changed only by UCXListener");
txt_ownqrgMainCategory.textProperty().bind(this.chatcontroller.getChatPreferences().getMYQRGFirstCat());
} else {
txt_ownqrgMainCategory.setTooltip(new Tooltip("enter your cq qrg here"));
// System.out.println("[Main.java, Info]: MYQRG will be changed only by User input");
txt_ownqrgMainCategory.textProperty().addListener((observable, oldValue, newValue) -> {
System.out.println(
"[Main.java, Info]: MYQRG Text changed from " + oldValue + " to " + newValue + " by hand");
MYQRGButton.textProperty().set(newValue);
});
}
grdPnltrx.add(generateLabeledSeparator(100, "Receive UCXLog TRX info"), 0, 0, 2, 1);
grdPnltrx.add(lblEnableTRXMsgbyUCX, 0, 1);
grdPnltrx.add(chkBxEnableTRXMsgbyUCX, 1, 1);
grdPnltrx.add(generateLabeledSeparator(100, "Win-Test TRX sync"), 0, 2, 2, 1);
grdPnltrx.add(lblWtQrgSync, 0, 3);
grdPnltrx.add(chkBxWtQrgSync, 1, 3);
grdPnltrx.add(lblWtUsePassQrg, 0, 4);
grdPnltrx.add(chkBxWtUsePassQrg, 1, 4);
grdPnltrx.add(lblWtStationFilter, 0, 5);
grdPnltrx.add(txtFldWtStationFilter, 1, 5);
VBox vbxTRXSynch = new VBox();
vbxTRXSynch.setPadding(new Insets(10, 10, 10, 10));
vbxTRXSynch.getChildren().addAll(grdPnltrx);
@@ -8124,6 +8186,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
else if (chatcontroller.isConnectedAndLoggedIn()) {
btnOptionspnlDisconnectOnly.setDisable(false);
menuItemFileDisconnect.setDisable(false);
menuItemFileConnect.setDisable(true);
menuItemOptionsAwayBack.setDisable(false);
}
@@ -8147,13 +8210,19 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
txtFldstn_maxQRBDefault.setDisable(false);
menuItemOptionsSetFrequencyAsName.setDisable(true);
menuItemOptionsAwayBack.setDisable(true);
menuItemFileConnect.setDisable(false);
station_chkBxEnableSecondChat.setDisable(false);
stn_choiceBxChatChategorySecond.setDisable(false);
}
});
btnOptionspnlConnect = new Button("Connect to " + chatcontroller.getChatPreferences().getLoginChatCategoryMain()
.getChatCategoryName(choiceBxChatChategory.getSelectionModel().getSelectedItem().getCategoryNumber()));
String btnText = "Connect to " + chatcontroller.getChatPreferences().getLoginChatCategoryMain()
.getChatCategoryName(choiceBxChatChategory.getSelectionModel().getSelectedItem().getCategoryNumber());
ChatCategory secCat = chatcontroller.getChatPreferences().getLoginChatCategorySecond();
if (chatcontroller.getChatPreferences().isLoginToSecondChatEnabled() && secCat != null) {
btnText += " & " + secCat.getChatCategoryName(secCat.getCategoryNumber());
}
btnOptionspnlConnect = new Button(btnText);
btnOptionspnlConnect.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
@@ -8185,6 +8254,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
btnOptionspnlDisconnectOnly.setDisable(false);
menuItemFileDisconnect.setDisable(false);
menuItemFileConnect.setDisable(true);
menuItemOptionsAwayBack.setDisable(false);
menuItemOptionsSetFrequencyAsName.setDisable(false);