From e37cda8ff2ea27b46325f45a3db0289c62ae8582 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 4 Aug 2026 21:10:15 +0200 Subject: [PATCH] Add 50/70 MHz band support (table, NOT-QRV, station setup, loggers) Issue #68 Adds Band.B_50/B_70 and wires them through the same band-opportunity machinery as the other bands: X/a/B+/o table columns and filter button in the User table and the Workedstn database table, NOT-QRV checkboxes and propagation across callsign variants, "My station uses 6m/4m band" toggles, station-name detection ("50", "6M", "70MHZ", "4M" - without stealing the existing bare "70"/"6" cm-band shorthand), Win-Test sked band IDs (10/50MHz, 11/70MHz), and UCX-logger worked-band recognition. Persists worked50/70 and notQRV50/70 via an additive SQLite migration (same ensureColumnExists pattern as the earlier v1.1->v1.2 migration), verified against a real database file. ReachabilityService.resolveAutoBand() now also falls back to 50 MHz (then 70 MHz) for stations in the "50/70 MHz" chat category, mirroring the existing Microwave-category fallback to 23cm. Assisted by Claude Sonnet 5 --- .../controller/ChatController.java | 8 + .../kst4contest/controller/DBController.java | 115 +++++++-- .../controller/ReachabilityService.java | 11 + .../controller/ReadUDPbyUCXMessageThread.java | 28 ++- .../controller/WinTestSkedSender.java | 2 + .../controller/WorkedGrossFieldCache.java | 2 + .../logic/BandOpportunityResolver.java | 11 + src/main/java/kst4contest/model/Band.java | 4 + .../java/kst4contest/model/ChatMember.java | 20 ++ .../kst4contest/model/ChatPreferences.java | 28 +++ .../view/Kst4ContestApplication.java | 234 +++++++++++++++++- .../map/MapCallsignRawSnapshotBuilder.java | 2 + .../test/BandOpportunityResolverTest.java | 23 ++ 13 files changed, 459 insertions(+), 29 deletions(-) diff --git a/src/main/java/kst4contest/controller/ChatController.java b/src/main/java/kst4contest/controller/ChatController.java index b165e4c..f54ed67 100644 --- a/src/main/java/kst4contest/controller/ChatController.java +++ b/src/main/java/kst4contest/controller/ChatController.java @@ -303,6 +303,8 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList private static String bandToHumanLabel(Band b) { if (b == null) return "?"; return switch (b) { + case B_50 -> "6m"; + case B_70 -> "4m"; case B_144 -> "2m"; case B_432 -> "70cm"; case B_1296 -> "23cm"; @@ -1318,6 +1320,8 @@ private ObservableList continue; } + target.setQrv50(sourceMember.isQrv50()); + target.setQrv70(sourceMember.isQrv70()); target.setQrv144(sourceMember.isQrv144()); target.setQrv432(sourceMember.isQrv432()); target.setQrv1240(sourceMember.isQrv1240()); @@ -2922,6 +2926,8 @@ private ObservableList activeChatMember.setWorked3400(storedChatMemberState.isWorked3400()); activeChatMember.setWorked5600(storedChatMemberState.isWorked5600()); activeChatMember.setWorked10G(storedChatMemberState.isWorked10G()); + activeChatMember.setWorked50(storedChatMemberState.isWorked50()); + activeChatMember.setWorked70(storedChatMemberState.isWorked70()); activeChatMember.setQrv144(storedChatMemberState.isQrv144()); activeChatMember.setQrv432(storedChatMemberState.isQrv432()); activeChatMember.setQrv1240(storedChatMemberState.isQrv1240()); @@ -2929,6 +2935,8 @@ private ObservableList activeChatMember.setQrv3400(storedChatMemberState.isQrv3400()); activeChatMember.setQrv5600(storedChatMemberState.isQrv5600()); activeChatMember.setQrv10G(storedChatMemberState.isQrv10G()); + activeChatMember.setQrv50(storedChatMemberState.isQrv50()); + activeChatMember.setQrv70(storedChatMemberState.isQrv70()); } } diff --git a/src/main/java/kst4contest/controller/DBController.java b/src/main/java/kst4contest/controller/DBController.java index 583d588..d6523b0 100644 --- a/src/main/java/kst4contest/controller/DBController.java +++ b/src/main/java/kst4contest/controller/DBController.java @@ -145,6 +145,7 @@ public class DBController { createWorkedGrossFieldTableIfRequired(); versionUpdateOfDBCheckAndChangeV11ToV12(); versionUpdateOfDBCheckAndChangeV12ToV13(); + versionUpdateOfDBCheckAndChangeV13ToV14(); if (helper_isDatabaseSchemaVersionOlderThanCurrent() || helper_isCallsignNormalizationMigrationRequired()) { normalizeStoredCallsignsToRawCallsigns(); @@ -230,6 +231,8 @@ public class DBController { + "worked3400 BOOLEAN DEFAULT 0, " + "worked5600 BOOLEAN DEFAULT 0, " + "worked10G BOOLEAN DEFAULT 0, " + + "worked50 BOOLEAN DEFAULT 0, " + + "worked70 BOOLEAN DEFAULT 0, " + "notQRV144 BOOLEAN DEFAULT 0, " + "notQRV432 BOOLEAN DEFAULT 0, " + "notQRV1240 BOOLEAN DEFAULT 0, " @@ -237,6 +240,8 @@ public class DBController { + "notQRV3400 BOOLEAN DEFAULT 0, " + "notQRV5600 BOOLEAN DEFAULT 0, " + "notQRV10G BOOLEAN DEFAULT 0, " + + "notQRV50 BOOLEAN DEFAULT 0, " + + "notQRV70 BOOLEAN DEFAULT 0, " + "lastFlagsChangeEpochMs INTEGER DEFAULT 0" + ");"; @@ -303,6 +308,22 @@ public class DBController { } } + /** + * Updates old v1.3 databases to the v1.4 schema by adding the worked/not-QRV + * columns for the 50 MHz and 70 MHz bands if they are missing. + */ + public synchronized void versionUpdateOfDBCheckAndChangeV13ToV14() { + + try { + ensureColumnExists("ChatMember", "worked50", "BOOLEAN DEFAULT 0"); + ensureColumnExists("ChatMember", "worked70", "BOOLEAN DEFAULT 0"); + ensureColumnExists("ChatMember", "notQRV50", "BOOLEAN DEFAULT 0"); + ensureColumnExists("ChatMember", "notQRV70", "BOOLEAN DEFAULT 0"); + } catch (SQLException e) { + throw new RuntimeException("[DBH, ERROR:] Could not migrate database from v1.3 to v1.4", e); + } + } + /** * Adds a missing column to an existing table. This method is used for safe schema * upgrades on customer systems which still contain older database files. @@ -452,7 +473,11 @@ public class DBController { targetChatMember.setWorked3400(targetChatMember.isWorked3400() || sourceChatMember.isWorked3400()); targetChatMember.setWorked5600(targetChatMember.isWorked5600() || sourceChatMember.isWorked5600()); targetChatMember.setWorked10G(targetChatMember.isWorked10G() || sourceChatMember.isWorked10G()); + targetChatMember.setWorked50(targetChatMember.isWorked50() || sourceChatMember.isWorked50()); + targetChatMember.setWorked70(targetChatMember.isWorked70() || sourceChatMember.isWorked70()); + targetChatMember.setQrv50(targetChatMember.isQrv50() && sourceChatMember.isQrv50()); + targetChatMember.setQrv70(targetChatMember.isQrv70() && sourceChatMember.isQrv70()); targetChatMember.setQrv144(targetChatMember.isQrv144() && sourceChatMember.isQrv144()); targetChatMember.setQrv432(targetChatMember.isQrv432() && sourceChatMember.isQrv432()); targetChatMember.setQrv1240(targetChatMember.isQrv1240() && sourceChatMember.isQrv1240()); @@ -491,6 +516,8 @@ public class DBController { + "worked3400 = 0, " + "worked5600 = 0, " + "worked10G = 0, " + + "worked50 = 0, " + + "worked70 = 0, " + "notQRV144 = 0, " + "notQRV432 = 0, " + "notQRV1240 = 0, " @@ -498,6 +525,8 @@ public class DBController { + "notQRV3400 = 0, " + "notQRV5600 = 0, " + "notQRV10G = 0, " + + "notQRV50 = 0, " + + "notQRV70 = 0, " + "lastFlagsChangeEpochMs = 0 " + "WHERE lastFlagsChangeEpochMs > 0 AND lastFlagsChangeEpochMs < ?;"; @@ -534,9 +563,9 @@ public class DBController { String insertOrUpdateSql = "INSERT INTO ChatMember (" - + "callsign, qra, name, lastActivityDateTime, worked, worked144, worked432, worked1240, worked2300, worked3400, worked5600, worked10G, " - + "notQRV144, notQRV432, notQRV1240, notQRV2300, notQRV3400, notQRV5600, notQRV10G, lastFlagsChangeEpochMs" - + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + + "callsign, qra, name, lastActivityDateTime, worked, worked144, worked432, worked1240, worked2300, worked3400, worked5600, worked10G, worked50, worked70, " + + "notQRV144, notQRV432, notQRV1240, notQRV2300, notQRV3400, notQRV5600, notQRV10G, notQRV50, notQRV70, lastFlagsChangeEpochMs" + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + "ON CONFLICT(callsign) DO UPDATE SET " + "qra = excluded.qra, " + "name = excluded.name, " @@ -557,14 +586,18 @@ public class DBController { preparedStatement.setInt(10, helper_booleanIntConverter(chatMemberToStore.isWorked3400())); preparedStatement.setInt(11, helper_booleanIntConverter(chatMemberToStore.isWorked5600())); preparedStatement.setInt(12, helper_booleanIntConverter(chatMemberToStore.isWorked10G())); - preparedStatement.setInt(13, helper_booleanIntConverter(!chatMemberToStore.isQrv144())); - preparedStatement.setInt(14, helper_booleanIntConverter(!chatMemberToStore.isQrv432())); - preparedStatement.setInt(15, helper_booleanIntConverter(!chatMemberToStore.isQrv1240())); - preparedStatement.setInt(16, helper_booleanIntConverter(!chatMemberToStore.isQrv2300())); - preparedStatement.setInt(17, helper_booleanIntConverter(!chatMemberToStore.isQrv3400())); - preparedStatement.setInt(18, helper_booleanIntConverter(!chatMemberToStore.isQrv5600())); - preparedStatement.setInt(19, helper_booleanIntConverter(!chatMemberToStore.isQrv10G())); - preparedStatement.setLong(20, resolvedLastFlagsChangeEpochMs); + preparedStatement.setInt(13, helper_booleanIntConverter(chatMemberToStore.isWorked50())); + preparedStatement.setInt(14, helper_booleanIntConverter(chatMemberToStore.isWorked70())); + preparedStatement.setInt(15, helper_booleanIntConverter(!chatMemberToStore.isQrv144())); + preparedStatement.setInt(16, helper_booleanIntConverter(!chatMemberToStore.isQrv432())); + preparedStatement.setInt(17, helper_booleanIntConverter(!chatMemberToStore.isQrv1240())); + preparedStatement.setInt(18, helper_booleanIntConverter(!chatMemberToStore.isQrv2300())); + preparedStatement.setInt(19, helper_booleanIntConverter(!chatMemberToStore.isQrv3400())); + preparedStatement.setInt(20, helper_booleanIntConverter(!chatMemberToStore.isQrv5600())); + preparedStatement.setInt(21, helper_booleanIntConverter(!chatMemberToStore.isQrv10G())); + preparedStatement.setInt(22, helper_booleanIntConverter(!chatMemberToStore.isQrv50())); + preparedStatement.setInt(23, helper_booleanIntConverter(!chatMemberToStore.isQrv70())); + preparedStatement.setLong(24, resolvedLastFlagsChangeEpochMs); preparedStatement.executeUpdate(); } catch (SQLException e) { System.err.println("[DBH, ERROR:] Chatmember could not been stored."); @@ -646,8 +679,8 @@ public class DBController { String resetAllWorkedDataSql = "UPDATE ChatMember SET " - + "worked = 0, worked144 = 0, worked432 = 0, worked1240 = 0, worked2300 = 0, worked3400 = 0, worked5600 = 0, worked10G = 0, " - + "notQRV144 = 0, notQRV432 = 0, notQRV1240 = 0, notQRV2300 = 0, notQRV3400 = 0, notQRV5600 = 0, notQRV10G = 0, " + + "worked = 0, worked144 = 0, worked432 = 0, worked1240 = 0, worked2300 = 0, worked3400 = 0, worked5600 = 0, worked10G = 0, worked50 = 0, worked70 = 0, " + + "notQRV144 = 0, notQRV432 = 0, notQRV1240 = 0, notQRV2300 = 0, notQRV3400 = 0, notQRV5600 = 0, notQRV10G = 0, notQRV50 = 0, notQRV70 = 0, " + "lastFlagsChangeEpochMs = 0;"; try (Statement statement = connection.createStatement()) { @@ -781,6 +814,8 @@ public class DBController { + "notQRV3400 = ?, " + "notQRV5600 = ?, " + "notQRV10G = ?, " + + "notQRV50 = ?, " + + "notQRV70 = ?, " + "lastFlagsChangeEpochMs = ? " + "WHERE callsign = ?;"; @@ -792,8 +827,10 @@ public class DBController { preparedStatement.setInt(5, helper_booleanIntConverter(!chatMemberToStore.isQrv3400())); preparedStatement.setInt(6, helper_booleanIntConverter(!chatMemberToStore.isQrv5600())); preparedStatement.setInt(7, helper_booleanIntConverter(!chatMemberToStore.isQrv10G())); - preparedStatement.setLong(8, System.currentTimeMillis()); - preparedStatement.setString(9, chatMemberToStore.getCallSignRaw()); + preparedStatement.setInt(8, helper_booleanIntConverter(!chatMemberToStore.isQrv50())); + preparedStatement.setInt(9, helper_booleanIntConverter(!chatMemberToStore.isQrv70())); + preparedStatement.setLong(10, System.currentTimeMillis()); + preparedStatement.setString(11, chatMemberToStore.getCallSignRaw()); int affectedRows = preparedStatement.executeUpdate(); @@ -821,9 +858,9 @@ public class DBController { String upsertCompleteRowSql = "INSERT INTO ChatMember (" - + "callsign, qra, name, lastActivityDateTime, worked, worked144, worked432, worked1240, worked2300, worked3400, worked5600, worked10G, " - + "notQRV144, notQRV432, notQRV1240, notQRV2300, notQRV3400, notQRV5600, notQRV10G, lastFlagsChangeEpochMs" - + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + + "callsign, qra, name, lastActivityDateTime, worked, worked144, worked432, worked1240, worked2300, worked3400, worked5600, worked10G, worked50, worked70, " + + "notQRV144, notQRV432, notQRV1240, notQRV2300, notQRV3400, notQRV5600, notQRV10G, notQRV50, notQRV70, lastFlagsChangeEpochMs" + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + "ON CONFLICT(callsign) DO UPDATE SET " + "qra = excluded.qra, " + "name = excluded.name, " @@ -836,6 +873,8 @@ public class DBController { + "worked3400 = excluded.worked3400, " + "worked5600 = excluded.worked5600, " + "worked10G = excluded.worked10G, " + + "worked50 = excluded.worked50, " + + "worked70 = excluded.worked70, " + "notQRV144 = excluded.notQRV144, " + "notQRV432 = excluded.notQRV432, " + "notQRV1240 = excluded.notQRV1240, " @@ -843,6 +882,8 @@ public class DBController { + "notQRV3400 = excluded.notQRV3400, " + "notQRV5600 = excluded.notQRV5600, " + "notQRV10G = excluded.notQRV10G, " + + "notQRV50 = excluded.notQRV50, " + + "notQRV70 = excluded.notQRV70, " + "lastFlagsChangeEpochMs = excluded.lastFlagsChangeEpochMs;"; try (PreparedStatement preparedStatement = connection.prepareStatement(upsertCompleteRowSql)) { @@ -858,14 +899,18 @@ public class DBController { preparedStatement.setInt(10, helper_booleanIntConverter(chatMemberToStore.isWorked3400())); preparedStatement.setInt(11, helper_booleanIntConverter(chatMemberToStore.isWorked5600())); preparedStatement.setInt(12, helper_booleanIntConverter(chatMemberToStore.isWorked10G())); - preparedStatement.setInt(13, helper_booleanIntConverter(!chatMemberToStore.isQrv144())); - preparedStatement.setInt(14, helper_booleanIntConverter(!chatMemberToStore.isQrv432())); - preparedStatement.setInt(15, helper_booleanIntConverter(!chatMemberToStore.isQrv1240())); - preparedStatement.setInt(16, helper_booleanIntConverter(!chatMemberToStore.isQrv2300())); - preparedStatement.setInt(17, helper_booleanIntConverter(!chatMemberToStore.isQrv3400())); - preparedStatement.setInt(18, helper_booleanIntConverter(!chatMemberToStore.isQrv5600())); - preparedStatement.setInt(19, helper_booleanIntConverter(!chatMemberToStore.isQrv10G())); - preparedStatement.setLong(20, chatMemberToStore.getLastFlagsChangeEpochMs()); + preparedStatement.setInt(13, helper_booleanIntConverter(chatMemberToStore.isWorked50())); + preparedStatement.setInt(14, helper_booleanIntConverter(chatMemberToStore.isWorked70())); + preparedStatement.setInt(15, helper_booleanIntConverter(!chatMemberToStore.isQrv144())); + preparedStatement.setInt(16, helper_booleanIntConverter(!chatMemberToStore.isQrv432())); + preparedStatement.setInt(17, helper_booleanIntConverter(!chatMemberToStore.isQrv1240())); + preparedStatement.setInt(18, helper_booleanIntConverter(!chatMemberToStore.isQrv2300())); + preparedStatement.setInt(19, helper_booleanIntConverter(!chatMemberToStore.isQrv3400())); + preparedStatement.setInt(20, helper_booleanIntConverter(!chatMemberToStore.isQrv5600())); + preparedStatement.setInt(21, helper_booleanIntConverter(!chatMemberToStore.isQrv10G())); + preparedStatement.setInt(22, helper_booleanIntConverter(!chatMemberToStore.isQrv50())); + preparedStatement.setInt(23, helper_booleanIntConverter(!chatMemberToStore.isQrv70())); + preparedStatement.setLong(24, chatMemberToStore.getLastFlagsChangeEpochMs()); preparedStatement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException("[DBH, ERROR:] Could not rebuild normalized ChatMember row", e); @@ -894,6 +939,8 @@ public class DBController { builtChatMember.setWorked3400(helper_IntToBooleanConverter(resultSet.getInt("worked3400"))); builtChatMember.setWorked5600(helper_IntToBooleanConverter(resultSet.getInt("worked5600"))); builtChatMember.setWorked10G(helper_IntToBooleanConverter(resultSet.getInt("worked10G"))); + builtChatMember.setWorked50(helper_IntToBooleanConverter(resultSet.getInt("worked50"))); + builtChatMember.setWorked70(helper_IntToBooleanConverter(resultSet.getInt("worked70"))); builtChatMember.setQrv144(!helper_IntToBooleanConverter(resultSet.getInt("notQRV144"))); builtChatMember.setQrv432(!helper_IntToBooleanConverter(resultSet.getInt("notQRV432"))); builtChatMember.setQrv1240(!helper_IntToBooleanConverter(resultSet.getInt("notQRV1240"))); @@ -901,6 +948,8 @@ public class DBController { builtChatMember.setQrv3400(!helper_IntToBooleanConverter(resultSet.getInt("notQRV3400"))); builtChatMember.setQrv5600(!helper_IntToBooleanConverter(resultSet.getInt("notQRV5600"))); builtChatMember.setQrv10G(!helper_IntToBooleanConverter(resultSet.getInt("notQRV10G"))); + builtChatMember.setQrv50(!helper_IntToBooleanConverter(resultSet.getInt("notQRV50"))); + builtChatMember.setQrv70(!helper_IntToBooleanConverter(resultSet.getInt("notQRV70"))); builtChatMember.setLastFlagsChangeEpochMs(resultSet.getLong("lastFlagsChangeEpochMs")); return builtChatMember; @@ -922,6 +971,8 @@ public class DBController { targetChatMember.setWorked3400(sourceChatMember.isWorked3400()); targetChatMember.setWorked5600(sourceChatMember.isWorked5600()); targetChatMember.setWorked10G(sourceChatMember.isWorked10G()); + targetChatMember.setWorked50(sourceChatMember.isWorked50()); + targetChatMember.setWorked70(sourceChatMember.isWorked70()); targetChatMember.setQrv144(sourceChatMember.isQrv144()); targetChatMember.setQrv432(sourceChatMember.isQrv432()); targetChatMember.setQrv1240(sourceChatMember.isQrv1240()); @@ -929,6 +980,8 @@ public class DBController { targetChatMember.setQrv3400(sourceChatMember.isQrv3400()); targetChatMember.setQrv5600(sourceChatMember.isQrv5600()); targetChatMember.setQrv10G(sourceChatMember.isQrv10G()); + targetChatMember.setQrv50(sourceChatMember.isQrv50()); + targetChatMember.setQrv70(sourceChatMember.isQrv70()); targetChatMember.setLastFlagsChangeEpochMs(sourceChatMember.getLastFlagsChangeEpochMs()); } @@ -954,6 +1007,10 @@ public class DBController { return "worked5600"; } else if (chatMemberToStore.isWorked10G()) { return "worked10G"; + } else if (chatMemberToStore.isWorked50()) { + return "worked50"; + } else if (chatMemberToStore.isWorked70()) { + return "worked70"; } return null; @@ -996,13 +1053,17 @@ public class DBController { || chatMemberToStore.isWorked3400() || chatMemberToStore.isWorked5600() || chatMemberToStore.isWorked10G() + || chatMemberToStore.isWorked50() + || chatMemberToStore.isWorked70() || !chatMemberToStore.isQrv144() || !chatMemberToStore.isQrv432() || !chatMemberToStore.isQrv1240() || !chatMemberToStore.isQrv2300() || !chatMemberToStore.isQrv3400() || !chatMemberToStore.isQrv5600() - || !chatMemberToStore.isQrv10G(); + || !chatMemberToStore.isQrv10G() + || !chatMemberToStore.isQrv50() + || !chatMemberToStore.isQrv70(); } /** diff --git a/src/main/java/kst4contest/controller/ReachabilityService.java b/src/main/java/kst4contest/controller/ReachabilityService.java index 721e3ce..b726b7f 100644 --- a/src/main/java/kst4contest/controller/ReachabilityService.java +++ b/src/main/java/kst4contest/controller/ReachabilityService.java @@ -284,6 +284,17 @@ public final class ReachabilityService { return Band.B_1296; } + if (member != null + && member.getChatCategory() != null + && member.getChatCategory().getCategoryNumber() == ChatCategory.FIFTYSEVENTYMHz) { + if (fallbackBands.contains(Band.B_50)) { + return Band.B_50; + } + if (fallbackBands.contains(Band.B_70)) { + return Band.B_70; + } + } + if (fallbackBands.contains(Band.B_144)) { return Band.B_144; } diff --git a/src/main/java/kst4contest/controller/ReadUDPbyUCXMessageThread.java b/src/main/java/kst4contest/controller/ReadUDPbyUCXMessageThread.java index e6f7a12..de7e63b 100644 --- a/src/main/java/kst4contest/controller/ReadUDPbyUCXMessageThread.java +++ b/src/main/java/kst4contest/controller/ReadUDPbyUCXMessageThread.java @@ -144,6 +144,12 @@ public class ReadUDPbyUCXMessageThread extends Thread { } 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; @@ -357,6 +363,20 @@ public class ReadUDPbyUCXMessageThread extends Thread { 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 { @@ -434,7 +454,13 @@ public class ReadUDPbyUCXMessageThread extends Thread { modifyThat.setWorked(true); - if (workedCall.isWorked144()) { + if (workedCall.isWorked50()) { + modifyThat.setWorked50(true); + + } else if (workedCall.isWorked70()) { + modifyThat.setWorked70(true); + + } else if (workedCall.isWorked144()) { modifyThat.setWorked144(true); } else if (workedCall.isWorked432()) { diff --git a/src/main/java/kst4contest/controller/WinTestSkedSender.java b/src/main/java/kst4contest/controller/WinTestSkedSender.java index 338909d..cfc1e06 100644 --- a/src/main/java/kst4contest/controller/WinTestSkedSender.java +++ b/src/main/java/kst4contest/controller/WinTestSkedSender.java @@ -155,6 +155,8 @@ public class WinTestSkedSender { public static int toWinTestBandId(Band band) { if (band == null) return 12; // default to 144 MHz return switch (band) { + case B_50 -> 10; + case B_70 -> 11; case B_144 -> 12; case B_432 -> 14; case B_1296 -> 16; diff --git a/src/main/java/kst4contest/controller/WorkedGrossFieldCache.java b/src/main/java/kst4contest/controller/WorkedGrossFieldCache.java index fd7feeb..a44dea8 100644 --- a/src/main/java/kst4contest/controller/WorkedGrossFieldCache.java +++ b/src/main/java/kst4contest/controller/WorkedGrossFieldCache.java @@ -98,6 +98,8 @@ public final class WorkedGrossFieldCache { if (member.isWorked3400()) addWorked(Band.B_3400, locator); if (member.isWorked5600()) addWorked(Band.B_5760, locator); if (member.isWorked10G()) addWorked(Band.B_10G, locator); + if (member.isWorked50()) addWorked(Band.B_50, locator); + if (member.isWorked70()) addWorked(Band.B_70, locator); } } diff --git a/src/main/java/kst4contest/logic/BandOpportunityResolver.java b/src/main/java/kst4contest/logic/BandOpportunityResolver.java index 8884709..8620718 100644 --- a/src/main/java/kst4contest/logic/BandOpportunityResolver.java +++ b/src/main/java/kst4contest/logic/BandOpportunityResolver.java @@ -87,6 +87,8 @@ public final class BandOpportunityResolver { return enabledBands; } + if (preferences.isStn_bandActive50()) enabledBands.add(Band.B_50); + if (preferences.isStn_bandActive70()) enabledBands.add(Band.B_70); if (preferences.isStn_bandActive144()) enabledBands.add(Band.B_144); if (preferences.isStn_bandActive432()) enabledBands.add(Band.B_432); if (preferences.isStn_bandActive1240()) enabledBands.add(Band.B_1296); @@ -149,6 +151,8 @@ public final class BandOpportunityResolver { } private static void collectWorkedBands(ChatMember member, EnumSet target) { + if (member.isWorked50()) target.add(Band.B_50); + if (member.isWorked70()) target.add(Band.B_70); if (member.isWorked144()) target.add(Band.B_144); if (member.isWorked432()) target.add(Band.B_432); if (member.isWorked1240()) target.add(Band.B_1296); @@ -160,6 +164,8 @@ public final class BandOpportunityResolver { } private static void collectNotQrvBands(ChatMember member, EnumSet target) { + if (!member.isQrv50()) target.add(Band.B_50); + if (!member.isQrv70()) target.add(Band.B_70); if (!member.isQrv144()) target.add(Band.B_144); if (!member.isQrv432()) target.add(Band.B_432); if (!member.isQrv1240()) target.add(Band.B_1296); @@ -173,6 +179,11 @@ public final class BandOpportunityResolver { private static Map createStationNameBandPatterns() { Map patterns = new EnumMap<>(Band.class); + // Bare "70" and bare "6" are already claimed by the 70cm/6cm shorthand below + // (their "CM" suffix is optional), so 4m/6m must require an explicit MHz/"M" + // suffix here to avoid misreading a cm-band shorthand as 70/50 MHz. + patterns.put(Band.B_50, bandPattern("50(?:\\s*MHZ)?|6\\s*M")); + patterns.put(Band.B_70, bandPattern("70\\s*MHZ|4\\s*M")); patterns.put(Band.B_144, bandPattern("144(?:\\s*MHZ)?|2(?:\\s*M)?")); patterns.put(Band.B_432, bandPattern("432(?:\\s*MHZ)?|70(?:\\s*CM)?")); patterns.put(Band.B_1296, bandPattern("1296(?:\\s*MHZ)?|23(?:\\s*CM)?")); diff --git a/src/main/java/kst4contest/model/Band.java b/src/main/java/kst4contest/model/Band.java index 41c0dca..8c1f2c5 100644 --- a/src/main/java/kst4contest/model/Band.java +++ b/src/main/java/kst4contest/model/Band.java @@ -5,6 +5,8 @@ package kst4contest.model; * Used for plausibility checks in the Smart Parser. */ public enum Band { + B_50(50.000, 54.000, "50"), + B_70(70.000, 70.500, "70"), B_144(144.000, 146.000, "144"), B_432(432.000, 434.000, "432"), B_1296(1296.000, 1298.000, "1296"), @@ -75,6 +77,8 @@ public enum Band { */ public String getDisplayLabel() { switch (this) { + case B_50: return "50"; + case B_70: return "70"; case B_144: return "144"; case B_432: return "432"; case B_1296: return "1296"; diff --git a/src/main/java/kst4contest/model/ChatMember.java b/src/main/java/kst4contest/model/ChatMember.java index 191d840..84f1363 100644 --- a/src/main/java/kst4contest/model/ChatMember.java +++ b/src/main/java/kst4contest/model/ChatMember.java @@ -72,6 +72,8 @@ public class ChatMember { /** * Chatmember is qrv at all band except we initialize anything other, depending to user entry */ + boolean qrv50 = true; + boolean qrv70 = true; boolean qrv144 = true; boolean qrv432 = true; boolean qrv1240 = true; @@ -228,6 +230,22 @@ public class ChatMember { worked10G = worked10g; } + public boolean isQrv50() { + return qrv50; + } + + public void setQrv50(boolean qrv50) { + this.qrv50 = qrv50; + } + + public boolean isQrv70() { + return qrv70; + } + + public void setQrv70(boolean qrv70) { + this.qrv70 = qrv70; + } + public boolean isQrv144() { return qrv144; } @@ -607,6 +625,8 @@ public class ChatMember { public void resetQRVInformationAtAllBands() { this.setQrvAny(true); + this.setQrv50(true); + this.setQrv70(true); this.setQrv144(true); this.setQrv432(true); this.setQrv1240(true); diff --git a/src/main/java/kst4contest/model/ChatPreferences.java b/src/main/java/kst4contest/model/ChatPreferences.java index e29e66e..7aa8f3c 100644 --- a/src/main/java/kst4contest/model/ChatPreferences.java +++ b/src/main/java/kst4contest/model/ChatPreferences.java @@ -197,6 +197,8 @@ public class ChatPreferences { boolean loginToSecondChatEnabled; DoubleProperty actualQTF = new SimpleDoubleProperty(360); // will be updated by user at runtime! + boolean stn_bandActive50; + boolean stn_bandActive70; boolean stn_bandActive144; boolean stn_bandActive432; boolean stn_bandActive1240; @@ -1504,6 +1506,14 @@ public class ChatPreferences { + Element stn_bandActive50 = doc.createElement("stn_bandActive50"); + stn_bandActive50.setTextContent(this.stn_bandActive50+""); + station.appendChild(stn_bandActive50); + + Element stn_bandActive70 = doc.createElement("stn_bandActive70"); + stn_bandActive70.setTextContent(this.stn_bandActive70+""); + station.appendChild(stn_bandActive70); + Element stn_bandActive144 = doc.createElement("stn_bandActive144"); stn_bandActive144.setTextContent(this.stn_bandActive144+""); station.appendChild(stn_bandActive144); @@ -2239,6 +2249,8 @@ public class ChatPreferences { ); // Band activity flags (introduced later; if missing -> keep defaults) + stn_bandActive50 = getBoolean(stationEl, stn_bandActive50, "stn_bandActive50"); + stn_bandActive70 = getBoolean(stationEl, stn_bandActive70, "stn_bandActive70"); stn_bandActive144 = getBoolean(stationEl, stn_bandActive144, "stn_bandActive144"); stn_bandActive432 = getBoolean(stationEl, stn_bandActive432, "stn_bandActive432"); stn_bandActive1240 = getBoolean(stationEl, stn_bandActive1240, "stn_bandActive1240"); @@ -2896,6 +2908,22 @@ public class ChatPreferences { return result; } + public boolean isStn_bandActive50() { + return stn_bandActive50; + } + + public void setStn_bandActive50(boolean stn_bandActive50) { + this.stn_bandActive50 = stn_bandActive50; + } + + public boolean isStn_bandActive70() { + return stn_bandActive70; + } + + public void setStn_bandActive70(boolean stn_bandActive70) { + this.stn_bandActive70 = stn_bandActive70; + } + public boolean isStn_bandActive144() { return stn_bandActive144; } diff --git a/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java index a2db431..b1bad7f 100644 --- a/src/main/java/kst4contest/view/Kst4ContestApplication.java +++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java @@ -570,6 +570,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL private String bandToHumanLabel(kst4contest.model.Band b) { // Human-friendly labels for VHF/UHF/microwave contesting return switch (b) { + case B_50 -> "6m"; + case B_70 -> "4m"; case B_144 -> "2m"; case B_432 -> "70cm"; case B_1296 -> "23cm"; @@ -772,6 +774,50 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL // * users qrv info setting will follow here + CheckBox furtherInfoPnl_chkbx_notQRV50 = new CheckBox("tag not qrv 50"); + furtherInfoPnl_chkbx_notQRV50.setSelected(!selectedCallSignInfoStageChatMember.isQrv50()); + furtherInfoPnl_chkbx_notQRV50.selectedProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { + if (!newValue) { + selectedCallSignInfoStageChatMember.setQrv50(true); + } else { + selectedCallSignInfoStageChatMember.setQrv50(false); + } + + try { + + chatcontroller.propagateNotQrvStateToActiveMembers(selectedCallSignInfoStageChatMember); + chatcontroller.getDbHandler().updateNotQRVInfoOnChatMember(selectedCallSignInfoStageChatMember); + GuiUtils.triggerGUIFilteredChatMemberListChange(chatcontroller); + } catch (Exception e) { + //do nothing, upodate was not possible + } + } + }); + + CheckBox furtherInfoPnl_chkbx_notQRV70 = new CheckBox("tag not qrv 70"); + furtherInfoPnl_chkbx_notQRV70.setSelected(!selectedCallSignInfoStageChatMember.isQrv70()); + furtherInfoPnl_chkbx_notQRV70.selectedProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { + if (!newValue) { + selectedCallSignInfoStageChatMember.setQrv70(true); + } else { + selectedCallSignInfoStageChatMember.setQrv70(false); + } + + try { + + chatcontroller.propagateNotQrvStateToActiveMembers(selectedCallSignInfoStageChatMember); + chatcontroller.getDbHandler().updateNotQRVInfoOnChatMember(selectedCallSignInfoStageChatMember); + GuiUtils.triggerGUIFilteredChatMemberListChange(chatcontroller); + } catch (Exception e) { + //do nothing, upodate was not possible + } + } + }); + CheckBox furtherInfoPnl_chkbx_notQRV144 = new CheckBox("tag not qrv 144"); furtherInfoPnl_chkbx_notQRV144.setSelected(!selectedCallSignInfoStageChatMember.isQrv144()); furtherInfoPnl_chkbx_notQRV144.selectedProperty().addListener(new ChangeListener() { @@ -933,6 +979,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL @Override public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { if (!newValue) { + selectedCallSignInfoStageChatMember.setQrv50(true); + selectedCallSignInfoStageChatMember.setQrv70(true); selectedCallSignInfoStageChatMember.setQrv144(true); selectedCallSignInfoStageChatMember.setQrv432(true); selectedCallSignInfoStageChatMember.setQrv1240(true); @@ -941,6 +989,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL selectedCallSignInfoStageChatMember.setQrv5600(true); selectedCallSignInfoStageChatMember.setQrv10G(true); } else { + selectedCallSignInfoStageChatMember.setQrv50(false); + selectedCallSignInfoStageChatMember.setQrv70(false); selectedCallSignInfoStageChatMember.setQrv144(false); selectedCallSignInfoStageChatMember.setQrv432(false); selectedCallSignInfoStageChatMember.setQrv1240(false); @@ -970,6 +1020,12 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL selectedCallSignDownerSiteGridPane.add(furtherInfoPnl_chkbx_notQRV3, 3,2,1,1); selectedCallSignDownerSiteGridPane.add(furtherInfoPnl_chkbx_notQRVall, 3,3,1,1); + if (!chatcontroller.getChatPreferences().isStn_bandActive50()) { + furtherInfoPnl_chkbx_notQRV50.setVisible(false); + } + if (!chatcontroller.getChatPreferences().isStn_bandActive70()) { + furtherInfoPnl_chkbx_notQRV70.setVisible(false); + } if (!chatcontroller.getChatPreferences().isStn_bandActive144()) { furtherInfoPnl_chkbx_notQRV144.setVisible(false); } @@ -1079,6 +1135,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL selectedCallSignTurnAntBtn, selectedCallSignShowQRZprofile, selectedCallSignShowQRZCqprofile, + furtherInfoPnl_chkbx_notQRV50, + furtherInfoPnl_chkbx_notQRV70, furtherInfoPnl_chkbx_notQRV144, furtherInfoPnl_chkbx_notQRV432, furtherInfoPnl_chkbx_notQRV23, @@ -1794,6 +1852,34 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL }); + TableColumn sixMCol_subcol = new TableColumn("50"); + sixMCol_subcol + .setCellValueFactory(new Callback, ObservableValue>() { + + @Override + public ObservableValue call(CellDataFeatures cellDataFeatures) { + return new SimpleStringProperty(formatBandCellStatus( + cellDataFeatures.getValue(), Band.B_50, cellDataFeatures.getValue().isWorked50() + )); + } + }); + sixMCol_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(28)); + sixMCol_subcol.setCellFactory(createBandStatusCellFactory(Band.B_50)); + + TableColumn fourMCol_subcol = new TableColumn("70"); + fourMCol_subcol + .setCellValueFactory(new Callback, ObservableValue>() { + + @Override + public ObservableValue call(CellDataFeatures cellDataFeatures) { + return new SimpleStringProperty(formatBandCellStatus( + cellDataFeatures.getValue(), Band.B_70, cellDataFeatures.getValue().isWorked70() + )); + } + }); + fourMCol_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(28)); + fourMCol_subcol.setCellFactory(createBandStatusCellFactory(Band.B_70)); + TableColumn vhfCol_subcol = new TableColumn("144"); vhfCol_subcol .setCellValueFactory(new Callback, ObservableValue>() { @@ -1902,6 +1988,18 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL wkd.setValue(""); + if (!cellDataFeatures.getValue().isQrv50()) { + wkd.setValue(wkd.getValue() + "6m "); + } else { + wkd.setValue(wkd.getValue().replace("6m ","")); + } + + if (!cellDataFeatures.getValue().isQrv70()) { + wkd.setValue(wkd.getValue() + "4m "); + } else { + wkd.setValue(wkd.getValue().replace("4m ","")); + } + if (!cellDataFeatures.getValue().isQrv144()) { wkd.setValue(wkd.getValue() + "144 "); } else { @@ -1974,6 +2072,12 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL * add now only cols which affects the used band of my station */ + if (chatcontroller.getChatPreferences().isStn_bandActive50()) { + workedCol.getColumns().add(sixMCol_subcol); + } + if (chatcontroller.getChatPreferences().isStn_bandActive70()) { + workedCol.getColumns().add(fourMCol_subcol); + } if (chatcontroller.getChatPreferences().isStn_bandActive144()) { workedCol.getColumns().add(vhfCol_subcol); } @@ -2832,6 +2936,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL Button selectedCallSignTurnAntBtn, Button selectedCallSignShowQRZprofile, Button selectedCallSignShowQRZCqprofile, + CheckBox furtherInfoPnl_chkbx_notQRV50, + CheckBox furtherInfoPnl_chkbx_notQRV70, CheckBox furtherInfoPnl_chkbx_notQRV144, CheckBox furtherInfoPnl_chkbx_notQRV432, CheckBox furtherInfoPnl_chkbx_notQRV23, @@ -2843,6 +2949,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL ) { for (CheckBox cb : Arrays.asList( + furtherInfoPnl_chkbx_notQRV50, + furtherInfoPnl_chkbx_notQRV70, furtherInfoPnl_chkbx_notQRV144, furtherInfoPnl_chkbx_notQRV432, furtherInfoPnl_chkbx_notQRV23, @@ -2902,6 +3010,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL notQrvFlow.setStyle("-fx-padding: 3; -fx-border-color: lightgrey; -fx-border-width: 1;"); notQrvFlow.getChildren().addAll( new Label("Not QRV:"), + furtherInfoPnl_chkbx_notQRV50, + furtherInfoPnl_chkbx_notQRV70, furtherInfoPnl_chkbx_notQRV144, furtherInfoPnl_chkbx_notQRV432, furtherInfoPnl_chkbx_notQRV23, @@ -4763,6 +4873,44 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL }); wkdAny_subcol.prefWidthProperty().bind(tbl_chatMemberWkdDBTable.widthProperty().divide(28)); + TableColumn sixMCol_subcol = new TableColumn("50"); + sixMCol_subcol + .setCellValueFactory(new Callback, ObservableValue>() { + + @Override + public ObservableValue call(CellDataFeatures cellDataFeatures) { + SimpleStringProperty wkd = new SimpleStringProperty(); + + if (cellDataFeatures.getValue().isWorked50()) { + wkd.setValue("X"); + } else { + wkd.setValue(""); + } + + return wkd; + } + }); + sixMCol_subcol.prefWidthProperty().bind(tbl_chatMemberWkdDBTable.widthProperty().divide(28)); + + TableColumn fourMCol_subcol = new TableColumn("70"); + fourMCol_subcol + .setCellValueFactory(new Callback, ObservableValue>() { + + @Override + public ObservableValue call(CellDataFeatures cellDataFeatures) { + SimpleStringProperty wkd = new SimpleStringProperty(); + + if (cellDataFeatures.getValue().isWorked70()) { + wkd.setValue("X"); + } else { + wkd.setValue(""); + } + + return wkd; + } + }); + fourMCol_subcol.prefWidthProperty().bind(tbl_chatMemberWkdDBTable.widthProperty().divide(28)); + TableColumn vhfCol_subcol = new TableColumn("144"); vhfCol_subcol .setCellValueFactory(new Callback, ObservableValue>() { @@ -4893,7 +5041,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL shf3_subcol.prefWidthProperty().bind(tbl_chatMemberWkdDBTable.widthProperty().divide(32)); - workedCol.getColumns().addAll(wkdAny_subcol, vhfCol_subcol, uhfCol_subcol, shf23_subcol, shf13_subcol, + workedCol.getColumns().addAll(wkdAny_subcol, sixMCol_subcol, fourMCol_subcol, vhfCol_subcol, uhfCol_subcol, shf23_subcol, shf13_subcol, shf9_subcol, shf6_subcol, shf3_subcol); // TODO: automatize enabling to users bandChoice tbl_chatMemberWkdDBTable.getColumns().addAll(callSignCol, workedCol); @@ -7705,6 +7853,54 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL } }); + ToggleButton btnTglwkd50 = new ToggleButton("50"); + + Predicate wkd50Predicate = new Predicate() { + @Override + public boolean test(ChatMember chatMember) { + + if (chatMember.isWorked50() || !chatMember.isQrv50()) { + return false; + } + else return true; + } + }; + btnTglwkd50.setOnAction(new EventHandler() { + + @Override + public void handle(ActionEvent actionEvent) { + if (btnTglwkd50.isSelected()) { + chatcontroller.getLst_chatMemberListFilterPredicates().add(wkd50Predicate); + } else { + chatcontroller.getLst_chatMemberListFilterPredicates().remove(wkd50Predicate); + } + } + }); + + ToggleButton btnTglwkd70 = new ToggleButton("70"); + + Predicate wkd70Predicate = new Predicate() { + @Override + public boolean test(ChatMember chatMember) { + + if (chatMember.isWorked70() || !chatMember.isQrv70()) { + return false; + } + else return true; + } + }; + btnTglwkd70.setOnAction(new EventHandler() { + + @Override + public void handle(ActionEvent actionEvent) { + if (btnTglwkd70.isSelected()) { + chatcontroller.getLst_chatMemberListFilterPredicates().add(wkd70Predicate); + } else { + chatcontroller.getLst_chatMemberListFilterPredicates().remove(wkd70Predicate); + } + } + }); + ToggleButton btnTglwkd144 = new ToggleButton("144"); Predicate wkd144Predicate = new Predicate() { @@ -7911,6 +8107,12 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL /** * add only filter buttons at the callsigntable which affects used bands */ + if (chatcontroller.getChatPreferences().isStn_bandActive50()) { + chatMemberTableFilterWorkedBandFiltersHbx.getChildren().add(btnTglwkd50); + } + if (chatcontroller.getChatPreferences().isStn_bandActive70()) { + chatMemberTableFilterWorkedBandFiltersHbx.getChildren().add(btnTglwkd70); + } if (chatcontroller.getChatPreferences().isStn_bandActive144()) { chatMemberTableFilterWorkedBandFiltersHbx.getChildren().add(btnTglwkd144); } @@ -8963,6 +9165,34 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL // "! ! ! ! Don“t forget to reset the worked stations information before starting a new contest ! ! ! !")); + CheckBox settings_chkbx_QRV50 = new CheckBox("My station uses 6m band"); + settings_chkbx_QRV50.setSelected(chatcontroller.getChatPreferences().isStn_bandActive50()); + settings_chkbx_QRV50.selectedProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { + chatcontroller.getChatPreferences().setStn_bandActive50( + settings_chkbx_QRV50.isSelected()); + System.out.println("[Main.java, Info]: setted my 50 qrv setting to: " + + chatcontroller.getChatPreferences().isStn_bandActive50()); + chatMemberTableFilterQTFAndQRBHbox.setVisible(false); + chatMemberTableFilterQTFAndQRBHbox.setVisible(true); + } + }); + + CheckBox settings_chkbx_QRV70 = new CheckBox("My station uses 4m band"); + settings_chkbx_QRV70.setSelected(chatcontroller.getChatPreferences().isStn_bandActive70()); + settings_chkbx_QRV70.selectedProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { + chatcontroller.getChatPreferences().setStn_bandActive70( + settings_chkbx_QRV70.isSelected()); + System.out.println("[Main.java, Info]: setted my 70 qrv setting to: " + + chatcontroller.getChatPreferences().isStn_bandActive70()); + chatMemberTableFilterQTFAndQRBHbox.setVisible(false); + chatMemberTableFilterQTFAndQRBHbox.setVisible(true); + } + }); + CheckBox settings_chkbx_QRV144 = new CheckBox("My station uses 2m band"); settings_chkbx_QRV144.setSelected(chatcontroller.getChatPreferences().isStn_bandActive144()); settings_chkbx_QRV144.selectedProperty().addListener(new ChangeListener() { @@ -9063,6 +9293,8 @@ 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.add(settings_chkbx_QRV50, 1, 3); + grdPnlStation_bands.add(settings_chkbx_QRV70, 2, 3); grdPnlStation_bands.setStyle(" -fx-border-color: lightgray;\n" + " -fx-vgap: 5;\n" + diff --git a/src/main/java/kst4contest/view/map/MapCallsignRawSnapshotBuilder.java b/src/main/java/kst4contest/view/map/MapCallsignRawSnapshotBuilder.java index 6091321..eef0592 100644 --- a/src/main/java/kst4contest/view/map/MapCallsignRawSnapshotBuilder.java +++ b/src/main/java/kst4contest/view/map/MapCallsignRawSnapshotBuilder.java @@ -275,6 +275,8 @@ public final class MapCallsignRawSnapshotBuilder { private String toBandDisplayLabel(Band band) { return switch (band) { + case B_50 -> "50"; + case B_70 -> "70"; case B_144 -> "144"; case B_432 -> "432"; case B_1296 -> "1296"; diff --git a/src/test/java/kst4contest/test/BandOpportunityResolverTest.java b/src/test/java/kst4contest/test/BandOpportunityResolverTest.java index b4d0c64..869a3d0 100644 --- a/src/test/java/kst4contest/test/BandOpportunityResolverTest.java +++ b/src/test/java/kst4contest/test/BandOpportunityResolverTest.java @@ -80,6 +80,29 @@ class BandOpportunityResolverTest { assertTrue(resolution.getAvailableBands().contains(Band.B_144)); } + @Test + void detectsFourAndSixMetersWithoutStealingCmShorthandBands() { + EnumSet bands = BandOpportunityResolver.detectBandsFromStationName( + "QRV 50 70cm 6M 4M" + ); + + assertEquals( + EnumSet.of(Band.B_50, Band.B_432, Band.B_70), + bands + ); + } + + @Test + void bareSeventyAndBareSixStillMeanCentimeterBands() { + EnumSet bands = BandOpportunityResolver.detectBandsFromStationName( + "QRV 70 6" + ); + + assertEquals(EnumSet.of(Band.B_432, Band.B_5760), bands); + assertFalse(bands.contains(Band.B_70)); + assertFalse(bands.contains(Band.B_50)); + } + @Test void opportunityRequiresAvailableEnabledAndUnworkedBand() { ChatMember station = new ChatMember();