refactored the ugly switch case statement in logged-band-recognition

This commit is contained in:
Marc Froehlich
2026-08-31 00:15:26 +02:00
parent 08b109106e
commit 6b29ffe3ba
10 changed files with 610 additions and 336 deletions
+4
View File
@@ -74,6 +74,10 @@ CR/LF framing, XML framing, ports/transports, callsign normalization and frequen
- 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.
- UCXLog-compatible QSO packets and Win-Test `ADDQSO` packets are converted into one validated external-QSO state. Logger-specific numeric, metre and centimetre values and Win-Test band IDs are normalised once; the resolved band is then the sole source for per-band Worked and worked-grid state.
- A missing or unknown logger band sets only the global Worked state. Worked-grid state requires both a recognised project band and a valid locator; no band or locator is inferred. Packets without a usable callsign are discarded without terminating the listener.
- External logger threads do not read or mutate the JavaFX user-list projection. `ChatController` applies global and per-band Worked state to every active variant of the base callsign on the JavaFX Application Thread before evaluating a band-upgrade notice.
- The established Win-Test handling for 24, 47 and 76 GHz remains unchanged. Their Worked flags are retained, while only frequencies represented by the project `Band` model can create worked-grid state.
### Terrain data providers
+2
View File
@@ -82,6 +82,8 @@ 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.
- **Logger-Bandwerte vereinheitlicht:** Numerische sowie Meter- und Zentimeterangaben aus UCXLog-kompatiblen QSO-Paketen und die Band-IDs von Win-Test werden einmal normalisiert und danach einheitlich für Worked-Markierungen und Worked-Großfelder verwendet. Dadurch setzen insbesondere `2320`, `5760` und `10368` zuverlässig ihre vorhandenen Bandmarkierungen. Bei einem fehlenden oder unbekannten Band bleibt es beim globalen Worked-Status.
- **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. Ein Datenbank-Reset verändert die Datei nicht, sodass enthaltene Rufzeichen bei der nächsten Auswertung erneut als gearbeitet markiert werden.
- **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.
+2
View File
@@ -82,6 +82,8 @@ 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.
- **Consistent logger band values:** Numeric, metre and centimetre values from UCXLog-compatible QSO packets and Win-Test band IDs are normalised once and then used consistently for Worked marks and worked grid squares. In particular, `2320`, `5760` and `10368` now reliably set their existing band marks. A missing or unknown band continues to set only the global Worked status.
- **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. A database reset does not change the file, so callsigns contained in it are marked as worked again during the next evaluation.
- **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.
@@ -2017,6 +2017,68 @@ private ObservableList<String>
return matchingMembers;
}
/**
* Applies a QSO received from an external logger to all active callsign
* variants. Active member objects back JavaFX views, so their state is changed
* only on the JavaFX Application Thread. The band-upgrade hint is evaluated
* afterwards and therefore sees the new Worked state.
*
* @param loggedQso validated external QSO
*/
public void applyExternalLoggedQso(ExternalLoggedQso loggedQso) {
if (loggedQso == null) {
return;
}
runOnFxThread(() -> {
int updatedMembers = markExternalLoggedQsoMembers(
activeChatMembersByCallAndCategory.values(), loggedQso);
if (updatedMembers == 0) {
return;
}
fireUserListUpdate("External logger Worked state updated");
try {
onExternalLogEntryReceived(loggedQso.getCallSign());
} catch (Exception exception) {
LOGGER.log(Level.WARNING,
"Band-upgrade hint failed after external logger update for "
+ loggedQso.getCallSign(), exception);
}
});
}
static int markExternalLoggedQsoMembers(
Collection<ChatMember> members,
ExternalLoggedQso loggedQso
) {
if (members == null || loggedQso == null) {
return 0;
}
String workedBaseCall = ChatMember.normalizeCallSignToBaseCallSign(
loggedQso.getCallSign());
if (workedBaseCall == null || workedBaseCall.isBlank()) {
return 0;
}
int updatedMembers = 0;
for (ChatMember member : members) {
if (member == null) {
continue;
}
String memberCall = member.getCallSignRaw() != null
? member.getCallSignRaw() : member.getCallSign();
String memberBaseCall = ChatMember.normalizeCallSignToBaseCallSign(memberCall);
if (memberBaseCall != null && memberBaseCall.equalsIgnoreCase(workedBaseCall)) {
loggedQso.applyToActiveMember(member);
updatedMembers++;
}
}
return updatedMembers;
}
/**
* Applies the global Worked state from the Simplelogfile to every active
* callsign variant with the same base callsign. UI-backed state is changed only
@@ -0,0 +1,116 @@
package kst4contest.controller;
import java.util.Optional;
import kst4contest.model.ChatMember;
/**
* Validated QSO state shared by external logger listeners and the controller.
*/
public final class ExternalLoggedQso {
private final String callSign;
private final LoggedQsoBand band;
private final String locator;
private final String source;
private ExternalLoggedQso(
String callSign,
LoggedQsoBand band,
String locator,
String source
) {
this.callSign = callSign;
this.band = band;
this.locator = locator;
this.source = source;
}
/**
* Creates a QSO only when the logger supplied a usable callsign. Missing band
* and locator values remain unavailable and never receive guessed defaults.
*
* @param rawCallSign callsign received from the logger
* @param band normalized band or {@code null}
* @param rawLocator locator received from the logger or {@code null}
* @param source logger name used for persistence diagnostics
* @return validated QSO, or an empty value for a missing callsign
*/
public static Optional<ExternalLoggedQso> create(
String rawCallSign,
LoggedQsoBand band,
String rawLocator,
String source
) {
if (rawCallSign == null) {
return Optional.empty();
}
String normalizedCallSign = rawCallSign.trim();
if (normalizedCallSign.isEmpty()) {
return Optional.empty();
}
String normalizedLocator = WorkedGrossFieldCache.extractLocator6(rawLocator);
return Optional.of(new ExternalLoggedQso(
normalizedCallSign,
band,
normalizedLocator,
source
));
}
public String getCallSign() {
return callSign;
}
public LoggedQsoBand getBand() {
return band;
}
public String getLocator() {
return locator;
}
public String getSource() {
return source;
}
/**
* Builds the database representation of this QSO.
*
* @return chat-member state with global and optional band Worked flags
*/
public ChatMember toWorkedChatMember() {
ChatMember workedCall = new ChatMember();
workedCall.setCallSign(callSign);
workedCall.setWorked(true);
if (band != null) {
band.applyWorkedFlag(workedCall);
}
if (locator != null) {
workedCall.setQra(locator);
}
return workedCall;
}
/**
* Applies this QSO to one active callsign variant.
*
* @param member active variant with the same base callsign
*/
public void applyToActiveMember(ChatMember member) {
if (member == null) {
return;
}
member.setWorked(true);
if (band != null) {
band.applyWorkedFlag(member);
}
if (locator != null
&& (member.getQra() == null
|| member.getQra().isBlank()
|| "unknown".equalsIgnoreCase(member.getQra()))) {
member.setQra(locator);
}
}
}
@@ -0,0 +1,129 @@
package kst4contest.controller;
import java.util.Locale;
import kst4contest.model.Band;
import kst4contest.model.ChatMember;
/**
* Normalized band of a QSO received from an external logger.
*
* <p>Logger protocols use different representations for the same amateur-radio
* band. UCXLog-compatible XML may contain a frequency-like number or a metre /
* centimetre label, while Win-Test uses its own numeric band IDs. Resolving the
* raw value once prevents the Worked flag and gross-field update from interpreting
* the same packet differently.</p>
*/
public enum LoggedQsoBand {
BAND_50(Band.B_50),
BAND_70(Band.B_70),
BAND_144(Band.B_144),
BAND_432(Band.B_432),
BAND_1296(Band.B_1296),
BAND_2320(Band.B_2320),
BAND_3400(Band.B_3400),
BAND_5760(Band.B_5760),
BAND_10G(Band.B_10G),
BAND_24G(Band.B_24G),
BAND_47G(null),
BAND_76G(null);
private final Band projectBand;
LoggedQsoBand(Band projectBand) {
this.projectBand = projectBand;
}
/**
* Resolves aliases used by UCXLog, N1MM+, DXLog.net, QARTest and compatible
* loggers. The input is trimmed and case-normalized exactly once here.
*
* @param rawBand band value received in the XML packet
* @return normalized band, or {@code null} for a missing or unknown value
*/
public static LoggedQsoBand fromLoggerValue(String rawBand) {
if (rawBand == null) {
return null;
}
String normalizedBand = rawBand.trim().toLowerCase(Locale.ROOT);
return switch (normalizedBand) {
case "50", "6m" -> BAND_50;
case "70", "4m" -> BAND_70;
case "144", "2m" -> BAND_144;
case "432", "70cm" -> BAND_432;
case "1240", "1296", "23cm" -> BAND_1296;
case "2300", "2320", "13cm" -> BAND_2320;
case "3400", "9cm" -> BAND_3400;
case "5600", "5760", "6cm" -> BAND_5760;
case "10g", "10368", "3cm" -> BAND_10G;
default -> null;
};
}
/**
* Resolves the established Win-Test ADDQSO band IDs. IDs 22 and 23 retain
* their existing Worked flags even though the project Band enum has no 47 GHz
* or 76 GHz value for gross-field storage.
*
* @param rawBandId Win-Test band ID
* @return normalized band, or {@code null} for a missing or unknown ID
*/
public static LoggedQsoBand fromWinTestBandId(String rawBandId) {
if (rawBandId == null) {
return null;
}
String normalizedBandId = rawBandId.trim();
return switch (normalizedBandId) {
case "10" -> BAND_50;
case "11" -> BAND_70;
case "12" -> BAND_144;
case "14" -> BAND_432;
case "16" -> BAND_1296;
case "17" -> BAND_2320;
case "18" -> BAND_3400;
case "19" -> BAND_5760;
case "20" -> BAND_10G;
case "21" -> BAND_24G;
case "22" -> BAND_47G;
case "23" -> BAND_76G;
default -> null;
};
}
/**
* Returns the common Band value used by worked-grid persistence.
*
* @return project band, or {@code null} where no matching Band value exists
*/
public Band getProjectBand() {
return projectBand;
}
/**
* Applies the established per-band Worked flag to a chat member.
*
* @param member worked station or active callsign variant
*/
public void applyWorkedFlag(ChatMember member) {
if (member == null) {
return;
}
switch (this) {
case BAND_50 -> member.setWorked50(true);
case BAND_70 -> member.setWorked70(true);
case BAND_144 -> member.setWorked144(true);
case BAND_432 -> member.setWorked432(true);
case BAND_1296 -> member.setWorked1240(true);
case BAND_2320 -> member.setWorked2300(true);
case BAND_3400 -> member.setWorked3400(true);
case BAND_5760 -> member.setWorked5600(true);
case BAND_10G -> member.setWorked10G(true);
case BAND_24G -> member.setWorked24G(true);
case BAND_47G -> member.setWorked47G(true);
case BAND_76G -> member.setWorked76G(true);
}
}
}
@@ -2,10 +2,8 @@ package kst4contest.controller;
import javafx.application.Platform;
import kst4contest.ApplicationConstants;
import kst4contest.model.Band;
import kst4contest.model.ChatMember;
import kst4contest.model.ThreadStateMessage;
import kst4contest.view.GuiUtils;
import java.io.BufferedWriter;
import java.io.File;
@@ -98,7 +96,7 @@ public class ReadUDPByWintestThread extends Thread {
}
}
private void processWinTestMessage(String msg) {
void processWinTestMessage(String msg) {
// System.out.println("Wintest-Message received: " + msg);
lastPacketTime = System.currentTimeMillis();
@@ -368,33 +366,28 @@ public class ReadUDPByWintestThread extends Thread {
/**
* Resolves the project Band enum from Win-Test band IDs.
* Extracts the unchanged Win-Test band ID from the unquoted ADDQSO fields.
*
* <p>Only bands that exist in the current Band enum are returned. The existing
* 50 MHz and 70 MHz values are resolved in the same way as the other supported
* VHF, UHF and microwave bands.</p>
*
* @param bandId Win-Test band id from ADDQSO
* @return matching Band or null
* @param message complete ADDQSO packet
* @return raw band ID or an empty value when the field is missing
*/
private Band helper_resolveBandFromWinTestBandId(String bandId) {
if (bandId == null) {
return null;
static String extractBandIdFromWinTestAddQso(String message) {
if (message == null) {
return "";
}
return switch (bandId.trim()) {
case "10" -> Band.B_50;
case "11" -> Band.B_70;
case "12" -> Band.B_144;
case "14" -> Band.B_432;
case "16" -> Band.B_1296;
case "17" -> Band.B_2320;
case "18" -> Band.B_3400;
case "19" -> Band.B_5760;
case "20" -> Band.B_10G;
case "21" -> Band.B_24G;
default -> null;
};
String[] quotedParts = message.split("\"");
if (quotedParts.length <= 6) {
return "";
}
String unquotedFields = quotedParts[6].trim();
if (unquotedFields.isEmpty()) {
return "";
}
String[] packetFields = unquotedFields.split("\\s+");
return packetFields.length > 3 ? packetFields[3] : "";
}
/**
@@ -449,112 +442,32 @@ public class ReadUDPByWintestThread extends Thread {
* @param msg
*/
private void parseAddQso(String msg) {
ChatMember modifyThat = null;
try {
// int qsoNumber = extractQsoNumber(msg);
// receivedQsos.put(qsoNumber, msg);
// lastKnownQso = Math.max(lastKnownQso, qsoNumber);
String callSignCatched = msg.split("\"") [7];
String[] quotedParts = msg == null ? new String[0] : msg.split("\"");
String callSign = quotedParts.length > 7 ? quotedParts[7] : "";
String rawBandId = extractBandIdFromWinTestAddQso(msg);
String locatorFromLogger = helper_resolveLocatorFromWinTestAddQso(msg);
ChatMember workedCall = new ChatMember();
workedCall.setCallSign(callSignCatched);
workedCall.setWorked(true); //its worked at this place, for sure!
if (locatorFromLogger != null) {
workedCall.setQra(locatorFromLogger);
LoggedQsoBand loggedBand = LoggedQsoBand.fromWinTestBandId(rawBandId);
ExternalLoggedQso loggedQso = ExternalLoggedQso.create(
callSign, loggedBand, locatorFromLogger, "WINTEST").orElse(null);
if (loggedQso == null) {
System.out.println("[WinTestUDPRcvr: warning] ADDQSO without usable callsign ignored");
return;
}
ArrayList<Integer> markTheseChattersAsWorked = client.checkListForChatMemberIndexesByCallSign(workedCall);
String bandId;
bandId = msg.split("\"")[6].split(" ")[4].trim();
Band workedBand = helper_resolveBandFromWinTestBandId(bandId);
switch (bandId) {
case "10" -> workedCall.setWorked50(true);
case "11" -> workedCall.setWorked70(true);
case "12" -> workedCall.setWorked144(true);
case "14" -> workedCall.setWorked432(true);
case "16" -> workedCall.setWorked1240(true);
case "17" -> workedCall.setWorked2300(true);
case "18" -> workedCall.setWorked3400(true);
case "19" -> workedCall.setWorked5600(true);
case "20" -> workedCall.setWorked10G(true);
case "21" -> workedCall.setWorked24G(true);
case "22" -> workedCall.setWorked47G(true);
case "23" -> workedCall.setWorked76G(true);
default -> System.out.println("[WinTestUDPRcvr: warning] Unbekannte Band-ID: " + bandId);
if (loggedBand == null && !rawBandId.isEmpty()) {
System.out.println("[WinTestUDPRcvr: warning] Unknown band ID: " + rawBandId);
}
if (workedBand != null && locatorFromLogger != null) {
this.client.registerWorkedGrossField(workedBand, locatorFromLogger, workedCall, "WINTEST");
}
if (!markTheseChattersAsWorked.isEmpty()) {
//Worked call is part of the current chatmember list
for (int index : markTheseChattersAsWorked) {
//iterate through the logged in chatmembers callsigns and set the worked markers
modifyThat = client.getLst_chatMemberList().get(index);
modifyThat.setWorked(true); //worked its for sure
if (locatorFromLogger != null
&& (modifyThat.getQra() == null
|| modifyThat.getQra().isBlank()
|| "unknown".equalsIgnoreCase(modifyThat.getQra()))) {
modifyThat.setQra(locatorFromLogger);
}
if (workedCall.isWorked50()) {
modifyThat.setWorked50(true);
} else if (workedCall.isWorked70()) {
modifyThat.setWorked70(true);
} else if (workedCall.isWorked144()) {
modifyThat.setWorked144(true);
} else if (workedCall.isWorked432()) {
modifyThat.setWorked432(true);
} else if (workedCall.isWorked1240()) {
modifyThat.setWorked1240(true);
} else if (workedCall.isWorked2300()) {
modifyThat.setWorked2300(true);
} else if (workedCall.isWorked3400()) {
modifyThat.setWorked3400(true);
} else if (workedCall.isWorked5600()) {
modifyThat.setWorked5600(true);
} else if (workedCall.isWorked10G()) {
modifyThat.setWorked10G(true);
} else if (workedCall.isWorked24G()) {
modifyThat.setWorked24G(true);
} else if (workedCall.isWorked47G()) {
modifyThat.setWorked47G(true);
} else if (workedCall.isWorked76G()) {
modifyThat.setWorked76G(true);
} else {
System.out.println("[WinTestUDPRcvr: warning] found no new worked-flag for this band: " + workedCall.getCallSignRaw() + bandId);
}
}
try {
GuiUtils.triggerGUIFilteredChatMemberListChange(client); //not clean at all
// trigger band-upgrade hint after log entry (Win-Test)
try {
client.onExternalLogEntryReceived(workedCall.getCallSignRaw());
} catch (Exception e) {
System.out.println("[WinTestUDPRcvr, warning]: band-upgrade hint failed: " + e.getMessage());
}
} catch (Exception IllegalStateException) {
//do nothing, as it works...
}
ChatMember workedCall = loggedQso.toWorkedChatMember();
if (loggedBand != null
&& loggedBand.getProjectBand() != null
&& locatorFromLogger != null) {
this.client.registerWorkedGrossField(
loggedBand.getProjectBand(), locatorFromLogger, workedCall, loggedQso.getSource());
}
client.applyExternalLoggedQso(loggedQso);
boolean isInChat = this.client.getDbHandler().updateWkdInfoOnChatMember(workedCall);
// This will update the worked info on a worked chatmember. DBHandler will
@@ -587,17 +500,9 @@ public class ReadUDPByWintestThread extends Thread {
bufwrtrRawMSGOut = new BufferedWriter(fileWriterPersistUDPToFile);
if (modifyThat != null) {
bufwrtrRawMSGOut.write("\n" + modifyThat.toString());
bufwrtrRawMSGOut.flush();
bufwrtrRawMSGOut.close();
} else {
bufwrtrRawMSGOut.write("\n" + workedCall.toString());
bufwrtrRawMSGOut.flush();
bufwrtrRawMSGOut.close();
}
bufwrtrRawMSGOut.write("\n" + workedCall.toString());
bufwrtrRawMSGOut.flush();
bufwrtrRawMSGOut.close();
System.out.println("[WinTest, Info: Marking Chatmember as worked: " + workedCall.toString());
@@ -3,7 +3,6 @@ package kst4contest.controller;
import java.io.*;
import java.net.*;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import javax.xml.XMLConstants;
@@ -13,7 +12,6 @@ import javax.xml.parsers.ParserConfigurationException;
import kst4contest.ApplicationConstants;
import kst4contest.model.ThreadStateMessage;
import kst4contest.view.GuiUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -132,54 +130,6 @@ public class ReadUDPbyUCXMessageThread extends Thread {
return WorkedGrossFieldCache.extractLocator6(helper_getOptionalElementText(element, "rcvnr"));
}
/**
* Resolves the project Band enum from logger band values.
*
* @param band logger band text
* @return matching Band or null
*/
private Band helper_resolveBandFromLoggerBand(String band) {
if (band == null) {
return null;
}
switch (band.trim()) {
case "50":
case "6m":
return Band.B_50;
case "70":
case "4m":
return Band.B_70;
case "144":
case "2m":
return Band.B_144;
case "432":
case "70cm":
return Band.B_432;
case "1240":
case "1296":
case "23cm":
return Band.B_1296;
case "2300":
case "2320":
case "13cm":
return Band.B_2320;
case "3400":
case "9cm":
return Band.B_3400;
case "5600":
case "5760":
case "6cm":
return Band.B_5760;
case "10G":
case "10368":
case "3cm":
return Band.B_10G;
default:
return null;
}
}
public void run() {
System.out.println("ReadUDPByUCXLogThread: started Thread for UCXLog getUDP");
@@ -295,8 +245,6 @@ public class ReadUDPbyUCXMessageThread extends Thread {
String udpMsg = helper_extractXmlPayload(udpPacketToProcess);
ChatMember modifyThat = null;
ThreadStateMessage threadStateMessage = new ThreadStateMessage(this.ThreadNickName, true, "received Message\n" + udpMsg, false);
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
@@ -342,95 +290,27 @@ public class ReadUDPbyUCXMessageThread extends Thread {
Element element = (Element) node;
String call = element.getElementsByTagName("call").item(0).getTextContent();
String band = helper_getOptionalElementText(element, "band");
String call = helper_getOptionalElementText(element, "call");
String rawBand = helper_getOptionalElementText(element, "band");
String gridSquare = helper_resolveLocatorFromContactInfo(element);
String points = helper_getOptionalElementText(element, "points");
LoggedQsoBand loggedBand = LoggedQsoBand.fromLoggerValue(rawBand);
ExternalLoggedQso loggedQso = ExternalLoggedQso.create(
call, loggedBand, gridSquare, "UCXLOG").orElse(null);
if (loggedQso == null) {
System.out.println("[ReadUDPFromUCX, warning]: QSO packet without usable callsign ignored");
continue;
}
System.out.println("[Readudp, info ]: received Current Element :" + node.getNodeName()
+ "call: " + call + " / " + band + " ----> " + points + " POINTS");
+ "call: " + call + " / " + rawBand + " ----> " + points + " POINTS");
// client.getChatPreferences().setBcn_contestScoreSum(Long.parseLong(points));
ChatMember workedCall = new ChatMember();
workedCall.setCallSign(call);
workedCall.setWorked(true);
if (gridSquare != null) {
workedCall.setQra(gridSquare);
}
Band workedBand = helper_resolveBandFromLoggerBand(band);
switch (band) {
case "50":
case "6m":
{
workedCall.setWorked50(true);
break;
}
case "70":
case "4m":
{
workedCall.setWorked70(true);
break;
}
case "144":
case "2m": //minos contest logger
{
workedCall.setWorked144(true);
break;
}
case "432":
case "70cm":
{
workedCall.setWorked432(true);
break;
}
case "1240": //ucxlog style
case "1296": //used for n1mm / Dxlog
case "23cm": //minos contest logger
{
workedCall.setWorked1240(true);
break;
}
case "2300":
case "13cm":
{
workedCall.setWorked2300(true);
break;
}
case "3400":
case "9cm":
{
workedCall.setWorked3400(true);
break;
}
case "5600":
case "6cm":
{
workedCall.setWorked5600(true);
break;
}
case "10G":
case "3cm":
{
workedCall.setWorked10G(true);
break;
}
default:
System.out.println("[ReadUDPFromUCX, Error:] unexpected band value: \"" + band + "\"");
break;
ChatMember workedCall = loggedQso.toWorkedChatMember();
Band workedBand = loggedBand == null ? null : loggedBand.getProjectBand();
if (loggedBand == null && !rawBand.isEmpty()) {
System.out.println("[ReadUDPFromUCX, warning]: unexpected band value: \"" + rawBand + "\"");
}
{
@@ -444,60 +324,7 @@ public class ReadUDPbyUCXMessageThread extends Thread {
// asd //TODO: Check if callsign and callsignraw is similar, then mark first and further via new checklistforchatmembermultiplemethod with array of indize
ArrayList<Integer> markTheseChattersAsWorked = client.checkListForChatMemberIndexesByCallSign(workedCall);
if (markTheseChattersAsWorked.isEmpty()) {
//Worked call is not part of the chatmember list
} else {
for (int index : markTheseChattersAsWorked) {
modifyThat = client.getLst_chatMemberList().get(index);
modifyThat.setWorked(true);
if (workedCall.isWorked50()) {
modifyThat.setWorked50(true);
} else if (workedCall.isWorked70()) {
modifyThat.setWorked70(true);
} else if (workedCall.isWorked144()) {
modifyThat.setWorked144(true);
} else if (workedCall.isWorked432()) {
modifyThat.setWorked432(true);
} else if (workedCall.isWorked1240()) {
modifyThat.setWorked1240(true);
} else if (workedCall.isWorked2300()) {
modifyThat.setWorked2300(true);
} else if (workedCall.isWorked3400()) {
modifyThat.setWorked3400(true);
} else if (workedCall.isWorked5600()) {
modifyThat.setWorked5600(true);
} else if (workedCall.isWorked10G()) {
modifyThat.setWorked10G(true);
}
}
try {
GuiUtils.triggerGUIFilteredChatMemberListChange(this.client);
// BEGIN PATCH: trigger band-upgrade hint after log entry (UCXLog)
try {
client.onExternalLogEntryReceived(workedCall.getCallSignRaw());
} catch (Exception e) {
System.out.println("[UCXUDPRcvr, warning]: band-upgrade hint failed: " + e.getMessage());
}
} catch (Exception IllegalStateException) {
//do nothing, as it works...
}
}
client.applyExternalLoggedQso(loggedQso);
/**
* old mechanic to markup worked stations in the chatmember table
@@ -574,7 +401,8 @@ public class ReadUDPbyUCXMessageThread extends Thread {
}
if (workedBand != null && gridSquare != null) {
this.client.registerWorkedGrossField(workedBand, gridSquare, workedCall, "UCXLOG");
this.client.registerWorkedGrossField(
workedBand, gridSquare, workedCall, loggedQso.getSource());
}
boolean isInChat = this.client.getDbHandler().updateWkdInfoOnChatMember(workedCall);
@@ -609,17 +437,9 @@ public class ReadUDPbyUCXMessageThread extends Thread {
bufwrtrRawMSGOut = new BufferedWriter(fileWriterPersistUDPToFile);
if (modifyThat != null) {
bufwrtrRawMSGOut.write("\n" + modifyThat.toString());
bufwrtrRawMSGOut.flush();
bufwrtrRawMSGOut.close();
} else {
bufwrtrRawMSGOut.write("\n" + workedCall.toString());
bufwrtrRawMSGOut.flush();
bufwrtrRawMSGOut.close();
}
bufwrtrRawMSGOut.write("\n" + workedCall.toString());
bufwrtrRawMSGOut.flush();
bufwrtrRawMSGOut.close();
}
}
@@ -0,0 +1,173 @@
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.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Stream;
import kst4contest.model.ChatMember;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class ExternalLoggedQsoTest {
@ParameterizedTest
@MethodSource("loggerBandAliases")
void resolvesEveryLoggerBandAlias(String rawValue, LoggedQsoBand expectedBand) {
assertEquals(expectedBand, LoggedQsoBand.fromLoggerValue(rawValue));
}
@ParameterizedTest
@MethodSource("winTestBandIds")
void resolvesEveryExistingWinTestBandId(String rawValue, LoggedQsoBand expectedBand) {
assertEquals(expectedBand, LoggedQsoBand.fromWinTestBandId(rawValue));
}
@Test
void unknownOrMissingBandRemainsUnavailable() {
assertNull(LoggedQsoBand.fromLoggerValue(null));
assertNull(LoggedQsoBand.fromLoggerValue(""));
assertNull(LoggedQsoBand.fromLoggerValue("unknown"));
assertNull(LoggedQsoBand.fromWinTestBandId(null));
assertNull(LoggedQsoBand.fromWinTestBandId("99"));
}
@Test
void missingCallsignIsRejectedBeforeAnyStateIsBuilt() {
assertTrue(ExternalLoggedQso.create(null, LoggedQsoBand.BAND_144, "JO50AA", "TEST").isEmpty());
assertTrue(ExternalLoggedQso.create(" ", LoggedQsoBand.BAND_144, "JO50AA", "TEST").isEmpty());
}
@Test
void missingBandSetsOnlyGlobalWorkedState() {
ExternalLoggedQso qso = qso("DL1ABC", null, null);
ChatMember workedCall = qso.toWorkedChatMember();
assertTrue(workedCall.isWorked());
assertFalse(workedCall.isWorked50());
assertFalse(workedCall.isWorked70());
assertFalse(workedCall.isWorked144());
assertFalse(workedCall.isWorked2300());
assertNull(workedCall.getQra());
}
@Test
void winTest50And70MhzIdsSetTheirExistingFlags() {
String qso50Packet = winTestAddQsoPacket("10", "DL1ABC");
String qso70Packet = winTestAddQsoPacket("11", "DL1ABC");
String band50Id = ReadUDPByWintestThread.extractBandIdFromWinTestAddQso(qso50Packet);
String band70Id = ReadUDPByWintestThread.extractBandIdFromWinTestAddQso(qso70Packet);
ChatMember worked50 = qso("DL1ABC", LoggedQsoBand.fromWinTestBandId(band50Id), null)
.toWorkedChatMember();
ChatMember worked70 = qso("DL1ABC", LoggedQsoBand.fromWinTestBandId(band70Id), null)
.toWorkedChatMember();
assertEquals("10", band50Id);
assertEquals("11", band70Id);
assertTrue(worked50.isWorked50());
assertTrue(worked70.isWorked70());
}
@Test
void winTest47And76GhzFlagsRemainAvailableWithoutInventingProjectBands() {
LoggedQsoBand band47 = LoggedQsoBand.fromWinTestBandId("22");
LoggedQsoBand band76 = LoggedQsoBand.fromWinTestBandId("23");
ChatMember worked47 = qso("DL1ABC", band47, "JO50AA").toWorkedChatMember();
ChatMember worked76 = qso("DL1ABC", band76, "JO50AA").toWorkedChatMember();
assertTrue(worked47.isWorked47G());
assertTrue(worked76.isWorked76G());
assertNull(band47.getProjectBand());
assertNull(band76.getProjectBand());
}
@Test
void normalized2320AliasUpdatesEveryActiveCallsignVariant() {
ChatMember firstVariant = member("9A0BB-2");
ChatMember secondVariant = member("9A0BB-70");
ChatMember unrelated = member("DL1ABC");
ExternalLoggedQso qso = qso(
"9A0BB", LoggedQsoBand.fromLoggerValue(" 2320 "), "JO50AA");
int updated = ChatController.markExternalLoggedQsoMembers(
List.of(firstVariant, secondVariant, unrelated), qso);
assertEquals(2, updated);
assertTrue(firstVariant.isWorked());
assertTrue(firstVariant.isWorked2300());
assertEquals("JO50AA", firstVariant.getQra());
assertTrue(secondVariant.isWorked());
assertTrue(secondVariant.isWorked2300());
assertEquals("JO50AA", secondVariant.getQra());
assertFalse(unrelated.isWorked());
}
private static Stream<Arguments> loggerBandAliases() {
return Stream.of(
Arguments.of("50", LoggedQsoBand.BAND_50),
Arguments.of("6m", LoggedQsoBand.BAND_50),
Arguments.of("70", LoggedQsoBand.BAND_70),
Arguments.of("4m", LoggedQsoBand.BAND_70),
Arguments.of("144", LoggedQsoBand.BAND_144),
Arguments.of("2m", LoggedQsoBand.BAND_144),
Arguments.of("432", LoggedQsoBand.BAND_432),
Arguments.of("70cm", LoggedQsoBand.BAND_432),
Arguments.of("1240", LoggedQsoBand.BAND_1296),
Arguments.of("1296", LoggedQsoBand.BAND_1296),
Arguments.of("23cm", LoggedQsoBand.BAND_1296),
Arguments.of("2300", LoggedQsoBand.BAND_2320),
Arguments.of(" 2320 ", LoggedQsoBand.BAND_2320),
Arguments.of("13cm", LoggedQsoBand.BAND_2320),
Arguments.of("3400", LoggedQsoBand.BAND_3400),
Arguments.of("9cm", LoggedQsoBand.BAND_3400),
Arguments.of("5600", LoggedQsoBand.BAND_5760),
Arguments.of(" 5760 ", LoggedQsoBand.BAND_5760),
Arguments.of("6cm", LoggedQsoBand.BAND_5760),
Arguments.of("10G", LoggedQsoBand.BAND_10G),
Arguments.of(" 10368 ", LoggedQsoBand.BAND_10G),
Arguments.of("3cm", LoggedQsoBand.BAND_10G)
);
}
private static Stream<Arguments> winTestBandIds() {
return Stream.of(
Arguments.of(" 10 ", LoggedQsoBand.BAND_50),
Arguments.of("11", LoggedQsoBand.BAND_70),
Arguments.of("12", LoggedQsoBand.BAND_144),
Arguments.of("14", LoggedQsoBand.BAND_432),
Arguments.of("16", LoggedQsoBand.BAND_1296),
Arguments.of("17", LoggedQsoBand.BAND_2320),
Arguments.of("18", LoggedQsoBand.BAND_3400),
Arguments.of("19", LoggedQsoBand.BAND_5760),
Arguments.of("20", LoggedQsoBand.BAND_10G),
Arguments.of("21", LoggedQsoBand.BAND_24G),
Arguments.of("22", LoggedQsoBand.BAND_47G),
Arguments.of("23", LoggedQsoBand.BAND_76G)
);
}
private static ExternalLoggedQso qso(
String callSign,
LoggedQsoBand band,
String locator
) {
return ExternalLoggedQso.create(callSign, band, locator, "TEST").orElseThrow();
}
private static ChatMember member(String callSign) {
ChatMember member = new ChatMember();
member.setCallSign(callSign);
return member;
}
private static String winTestAddQsoPacket(String bandId, String callSign) {
return "ADDQSO: \"STN1\" \"\" \"STN1\" 1762202297 1440000 0 "
+ bandId + " 0 0 0 2 2 \"" + callSign
+ "\" \"599\" \"599001\" \"JO51UM\" \"\" \"\" 0 \"\" \"\" \"\" 44510";
}
}
@@ -0,0 +1,61 @@
package kst4contest.controller;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class ExternalLoggerListenerValidationTest {
@Mock
ChatController controller;
@Mock
ChatPreferences preferences;
@Mock
ThreadStatusCallback statusCallback;
@BeforeEach
void configurePreferences() {
when(controller.getChatPreferences()).thenReturn(preferences);
}
@Test
void ucxLogPacketWithoutCallsignIsDiscardedWithoutStoppingProcessing() {
when(preferences.isLogsynch_ucxUDPWkdCallListenerEnabled()).thenReturn(true);
ReadUDPbyUCXMessageThread listener = new ReadUDPbyUCXMessageThread(
12060, controller, statusCallback);
String packet = "<?xml version=\"1.0\"?><contactinfo>"
+ "<band>144</band><call> </call><gridsquare>JO50AA</gridsquare>"
+ "</contactinfo>";
assertDoesNotThrow(() -> listener.processUCXUDPMessage(packet));
verify(controller, never()).applyExternalLoggedQso(any());
verify(controller, never()).getDbHandler();
}
@Test
void winTestPacketWithoutCallsignIsDiscardedWithoutStoppingProcessing() {
when(preferences.getStn_loginCallSignRaw()).thenReturn("DL0TEST");
when(preferences.getLogsynch_wintestNetworkPort()).thenReturn(9871);
ReadUDPByWintestThread listener = new ReadUDPByWintestThread(controller, statusCallback);
String packet = "ADDQSO: \"STN1\" \"\" \"STN1\" 1762202297 1440000 0 12 0 0 0 2 2 "
+ "\" \" \"599\" \"599001\" \"JO51UM\" \"\" \"\" 0 \"\" \"\" \"\" 44510";
assertDoesNotThrow(() -> listener.processWinTestMessage(packet));
verify(controller, never()).applyExternalLoggedQso(any());
verify(controller, never()).getDbHandler();
}
}