Fixed beacon implementation, pstrotator settings, Variable handling, Shortcut and snipped preferences ui, beacon handling, AS Settings and all fitting manual texts
This commit is contained in:
@@ -1,122 +1,178 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import kst4contest.model.ChatMessage;
|
||||
import kst4contest.model.ThreadStateMessage;
|
||||
|
||||
/**
|
||||
* This class is for sending beacons intervalled to the public chat. Gets all
|
||||
* preferences and instances via the Chatpreferences-object of the
|
||||
* Chatcontroller.
|
||||
* <br/><br/>
|
||||
* The task will be runned out of the singleton ChatController instance in an
|
||||
* intervall as specified by the Chatpreferences-instance (typically as
|
||||
* configured in the xml file.
|
||||
*
|
||||
*
|
||||
* @author prakt
|
||||
* Sends the configured public-chat beacons for both active chat categories.
|
||||
*
|
||||
* <p>Both categories deliberately share one timer and therefore the same
|
||||
* interval. Their enable flags and message templates remain independent. Every
|
||||
* run reads the current preferences, resolves global message variables and
|
||||
* sends only the categories which are currently enabled.</p>
|
||||
*/
|
||||
public class BeaconTask extends TimerTask {
|
||||
|
||||
private ChatController chatController;
|
||||
private ThreadStatusCallback callBackToController;
|
||||
private String ThreadNickName = "MyBeacon";
|
||||
private static final String THREAD_NICKNAME = "MyBeacon";
|
||||
|
||||
public BeaconTask(ChatController client, ThreadStatusCallback callback) {
|
||||
this.callBackToController = callback;
|
||||
this.chatController = client;
|
||||
private final ChatController chatController;
|
||||
private final ThreadStatusCallback callbackToController;
|
||||
|
||||
/**
|
||||
* Creates one execution of the shared beacon timer.
|
||||
*
|
||||
* @param chatController controller providing preferences and the TX queue
|
||||
* @param callbackToController callback used by the thread-status display
|
||||
*/
|
||||
public BeaconTask(
|
||||
ChatController chatController,
|
||||
ThreadStatusCallback callbackToController
|
||||
) {
|
||||
this.chatController = chatController;
|
||||
this.callbackToController = callbackToController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Thread.currentThread().setName("BeaconTask");
|
||||
reportStatus(THREAD_NICKNAME, true, "initialized", false);
|
||||
|
||||
ThreadStateMessage threadStateMessage = new ThreadStateMessage(this.ThreadNickName, true, "initialized", false);
|
||||
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
|
||||
|
||||
Thread.currentThread().setName("BeaconTask");
|
||||
|
||||
|
||||
ChatMessage beaconMSG = new ChatMessage();
|
||||
|
||||
String replaceVariables = this.chatController.getChatPreferences().getBcn_beaconTextMainCat();
|
||||
|
||||
replaceVariables = replaceVariables.replaceAll("MYQRG", this.chatController.getChatPreferences().getMYQRGFirstCat().getValue());
|
||||
replaceVariables = replaceVariables.replaceAll("MYCALL", this.chatController.getChatPreferences().getStn_loginCallSign());
|
||||
replaceVariables = replaceVariables.replaceAll("MYLOCATOR", this.chatController.getChatPreferences().getStn_loginLocatorMainCat());
|
||||
replaceVariables = replaceVariables.replaceAll("MYQTF", this.chatController.getChatPreferences().getActualQTF().getValue() + "");
|
||||
replaceVariables = replaceVariables.replaceAll("SECONDQRG", this.chatController.getChatPreferences().getMYQRGSecondCat().getValue() + "");
|
||||
|
||||
|
||||
|
||||
beaconMSG.setMessageText(
|
||||
"MSG|" + this.chatController.getChatPreferences().getLoginChatCategoryMain().getCategoryNumber() + "|0|" + replaceVariables + "|0|");
|
||||
beaconMSG.setMessageDirectedToServer(true);
|
||||
|
||||
|
||||
|
||||
|
||||
ChatMessage beaconMSG2 = new ChatMessage();
|
||||
|
||||
String replaceVariables2 = this.chatController.getChatPreferences().getBcn_beaconTextSecondCat();
|
||||
|
||||
replaceVariables2 = replaceVariables2.replaceAll("MYQRG", this.chatController.getChatPreferences().getMYQRGFirstCat().getValue());
|
||||
replaceVariables2 = replaceVariables2.replaceAll("MYCALL", this.chatController.getChatPreferences().getStn_loginCallSign());
|
||||
replaceVariables2 = replaceVariables2.replaceAll("MYLOCATOR", this.chatController.getChatPreferences().getStn_loginLocatorMainCat());
|
||||
replaceVariables2 = replaceVariables2.replaceAll("MYQTF", this.chatController.getChatPreferences().getActualQTF().getValue() + "");
|
||||
replaceVariables2 = replaceVariables2.replaceAll("SECONDQRG", this.chatController.getChatPreferences().getMYQRGSecondCat().getValue() + "");
|
||||
|
||||
|
||||
beaconMSG2.setMessageText(
|
||||
"MSG|" + this.chatController.getChatPreferences().getLoginChatCategorySecond().getCategoryNumber() + "|0|" + replaceVariables + "|0|");
|
||||
beaconMSG2.setMessageDirectedToServer(true);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* beacon 1st Chatcategory
|
||||
*/
|
||||
if (this.chatController.getChatPreferences().isBcn_beaconsEnabledMainCat() ) {
|
||||
|
||||
System.out.println(new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
|
||||
+ " [BeaconTask, Info]: Sending CQ: " + beaconMSG.getMessageText());
|
||||
this.chatController.getMessageTXBus().add(beaconMSG);
|
||||
|
||||
threadStateMessage = new ThreadStateMessage(this.ThreadNickName + " 1", true, "on", false);
|
||||
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
|
||||
|
||||
} else {
|
||||
threadStateMessage = new ThreadStateMessage(this.ThreadNickName + " 1", false, "off", false);
|
||||
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* beacon 2nd Chatcategory
|
||||
*/
|
||||
if (this.chatController.getChatPreferences().isLoginToSecondChatEnabled()) { //only send if 2nd cat enabled
|
||||
|
||||
if (this.chatController.getChatPreferences().isBcn_beaconsEnabledSecondCat()) {
|
||||
|
||||
beaconMSG2.setMessageText(
|
||||
"MSG|" + this.chatController.getChatPreferences().getLoginChatCategorySecond().getCategoryNumber() + "|0|" + replaceVariables2 + "|0|");
|
||||
beaconMSG2.setMessageDirectedToServer(true);
|
||||
|
||||
System.out.println(new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
|
||||
+ " [BeaconTask, Info]: Sending CQ 2nd Cat: " + beaconMSG2.getMessageText());
|
||||
this.chatController.getMessageTXBus().add(beaconMSG2);
|
||||
|
||||
threadStateMessage = new ThreadStateMessage(this.ThreadNickName + " 2", true, "on", false);
|
||||
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
|
||||
|
||||
} else {
|
||||
threadStateMessage = new ThreadStateMessage(this.ThreadNickName + " 2", false, "off", false);
|
||||
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
|
||||
}
|
||||
}
|
||||
MessageVariableResolver variableResolver =
|
||||
new MessageVariableResolver(chatController.getChatPreferences());
|
||||
|
||||
sendMainCategoryBeacon(variableResolver);
|
||||
sendSecondCategoryBeacon(variableResolver);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Sends the main-category beacon if it is currently enabled.
|
||||
*/
|
||||
private void sendMainCategoryBeacon(MessageVariableResolver variableResolver) {
|
||||
if (!chatController.getChatPreferences().isBcn_beaconsEnabledMainCat()) {
|
||||
reportStatus(THREAD_NICKNAME + " 1", false, "off", false);
|
||||
return;
|
||||
}
|
||||
|
||||
String resolvedText = variableResolver.resolveGlobalVariables(
|
||||
chatController.getChatPreferences().getBcn_beaconTextMainCat()
|
||||
);
|
||||
ChatMessage beaconMessage = buildBeaconMessage(
|
||||
chatController.getChatPreferences()
|
||||
.getLoginChatCategoryMain()
|
||||
.getCategoryNumber(),
|
||||
resolvedText,
|
||||
"main category"
|
||||
);
|
||||
|
||||
if (beaconMessage == null) {
|
||||
reportStatus(THREAD_NICKNAME + " 1", false, "invalid text", true);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println(
|
||||
new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
|
||||
+ " [BeaconTask, Info]: Sending main-category CQ: "
|
||||
+ beaconMessage.getMessageText()
|
||||
);
|
||||
chatController.getMessageTXBus().add(beaconMessage);
|
||||
reportStatus(THREAD_NICKNAME + " 1", true, "on", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the second-category beacon if the second login and its beacon are
|
||||
* currently enabled.
|
||||
*/
|
||||
private void sendSecondCategoryBeacon(
|
||||
MessageVariableResolver variableResolver
|
||||
) {
|
||||
if (!chatController.getChatPreferences().isLoginToSecondChatEnabled()
|
||||
|| !chatController.getChatPreferences()
|
||||
.isBcn_beaconsEnabledSecondCat()) {
|
||||
reportStatus(THREAD_NICKNAME + " 2", false, "off", false);
|
||||
return;
|
||||
}
|
||||
|
||||
String resolvedText = variableResolver.resolveGlobalVariables(
|
||||
chatController.getChatPreferences().getBcn_beaconTextSecondCat()
|
||||
);
|
||||
ChatMessage beaconMessage = buildBeaconMessage(
|
||||
chatController.getChatPreferences()
|
||||
.getLoginChatCategorySecond()
|
||||
.getCategoryNumber(),
|
||||
resolvedText,
|
||||
"second category"
|
||||
);
|
||||
|
||||
if (beaconMessage == null) {
|
||||
reportStatus(THREAD_NICKNAME + " 2", false, "invalid text", true);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println(
|
||||
new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
|
||||
+ " [BeaconTask, Info]: Sending second-category CQ: "
|
||||
+ beaconMessage.getMessageText()
|
||||
);
|
||||
chatController.getMessageTXBus().add(beaconMessage);
|
||||
reportStatus(THREAD_NICKNAME + " 2", true, "on", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the server-directed message after validating the resolved payload.
|
||||
*
|
||||
* <p>The resolved text is checked rather than only the configured template
|
||||
* because inserted values can increase the final message length.</p>
|
||||
*
|
||||
* @param categoryNumber ON4KST category number
|
||||
* @param resolvedText fully resolved beacon payload
|
||||
* @param categoryDescription text used in diagnostic output
|
||||
* @return prepared message, or {@code null} if the payload is invalid
|
||||
*/
|
||||
private ChatMessage buildBeaconMessage(
|
||||
int categoryNumber,
|
||||
String resolvedText,
|
||||
String categoryDescription
|
||||
) {
|
||||
if (resolvedText == null
|
||||
|| resolvedText.length() > ChatController.MAX_BEACON_TEXT_LENGTH) {
|
||||
int actualLength = resolvedText == null ? 0 : resolvedText.length();
|
||||
System.out.println(
|
||||
"[BeaconTask, Warning]: Beacon for "
|
||||
+ categoryDescription
|
||||
+ " was not sent because the resolved text contains "
|
||||
+ actualLength
|
||||
+ " characters; maximum is "
|
||||
+ ChatController.MAX_BEACON_TEXT_LENGTH
|
||||
+ "."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
ChatMessage beaconMessage = new ChatMessage();
|
||||
beaconMessage.setMessageText(
|
||||
"MSG|" + categoryNumber + "|0|" + resolvedText + "|0|"
|
||||
);
|
||||
beaconMessage.setMessageDirectedToServer(true);
|
||||
return beaconMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards one state update to the existing thread-status display.
|
||||
*/
|
||||
private void reportStatus(
|
||||
String threadName,
|
||||
boolean running,
|
||||
String information,
|
||||
boolean criticalState
|
||||
) {
|
||||
ThreadStateMessage stateMessage = new ThreadStateMessage(
|
||||
threadName,
|
||||
running,
|
||||
information,
|
||||
criticalState
|
||||
);
|
||||
callbackToController.onThreadStatus(THREAD_NICKNAME, stateMessage);
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,10 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
|
||||
private static final boolean DEBUG_BAND_UPGRADE_HINT = true; //for new band hint
|
||||
|
||||
|
||||
public static final int MIN_BEACON_INTERVAL_MINUTES = 1;
|
||||
public static final int MAX_BEACON_TEXT_LENGTH = 120;
|
||||
private static final long INITIAL_BEACON_DELAY_MILLIS = 10_000L;
|
||||
|
||||
private PstRotatorClient rotatorClient;
|
||||
private Consumer<Double> viewRotorCallback;
|
||||
|
||||
@@ -708,8 +712,7 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
|
||||
// writeThread.interrupt();
|
||||
// readThread.interrupt();
|
||||
|
||||
beaconTimer.purge();
|
||||
beaconTimer.cancel();
|
||||
stopBeaconTimer();
|
||||
ASQueryTimer.purge();
|
||||
ASQueryTimer.cancel();
|
||||
socketCheckTimer.purge();
|
||||
@@ -2239,6 +2242,78 @@ private ObservableList<String>
|
||||
this.dbHandler = dbHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the shared beacon timer with the interval currently stored in the
|
||||
* preferences.
|
||||
*
|
||||
* <p>Both chat categories deliberately use the same timer. The task checks the
|
||||
* individual enable flag and text for each category every time it runs.</p>
|
||||
*
|
||||
* @param initialDelayMillis delay before the next beacon run
|
||||
*/
|
||||
private synchronized void scheduleBeaconTimer(long initialDelayMillis) {
|
||||
stopBeaconTimer();
|
||||
|
||||
int configuredInterval =
|
||||
chatPreferences.getBcn_beaconIntervalInMinutesMainCat();
|
||||
int effectiveInterval =
|
||||
Math.max(MIN_BEACON_INTERVAL_MINUTES, configuredInterval);
|
||||
|
||||
/*
|
||||
* Keep both legacy XML values synchronized. The second value remains in the
|
||||
* configuration for backward compatibility, but no longer represents an
|
||||
* independent timer.
|
||||
*/
|
||||
chatPreferences.setBcn_beaconIntervalInMinutesMainCat(effectiveInterval);
|
||||
chatPreferences.setBcn_beaconIntervalInMinutesSecondCat(effectiveInterval);
|
||||
|
||||
long intervalMillis = TimeUnit.MINUTES.toMillis(effectiveInterval);
|
||||
long safeInitialDelay = Math.max(0L, initialDelayMillis);
|
||||
|
||||
beaconTimer = new Timer("BeaconTimer");
|
||||
beaconTimer.schedule(
|
||||
new BeaconTask(this, this),
|
||||
safeInitialDelay,
|
||||
intervalMillis
|
||||
);
|
||||
|
||||
System.out.println("[ChatController, Info]: Shared beacon timer scheduled every "
|
||||
+ effectiveInterval + " minute(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a changed beacon interval while the chat connection is running.
|
||||
*
|
||||
* <p>The countdown starts again with the new interval. No beacon is sent
|
||||
* immediately merely because the setting was changed.</p>
|
||||
*/
|
||||
public synchronized void restartBeaconTimer() {
|
||||
if (!isConnectedAndLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
long intervalMillis = TimeUnit.MINUTES.toMillis(
|
||||
Math.max(
|
||||
MIN_BEACON_INTERVAL_MINUTES,
|
||||
chatPreferences.getBcn_beaconIntervalInMinutesMainCat()
|
||||
)
|
||||
);
|
||||
scheduleBeaconTimer(intervalMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the shared beacon timer if it is currently running.
|
||||
*/
|
||||
private synchronized void stopBeaconTimer() {
|
||||
if (beaconTimer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
beaconTimer.cancel();
|
||||
beaconTimer.purge();
|
||||
beaconTimer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* execute is the main entry point where the application starts.
|
||||
* @throws InterruptedException
|
||||
@@ -2338,11 +2413,7 @@ private ObservableList<String>
|
||||
* The CQ-beacon-Task will be executed every time but checks for itself whether
|
||||
* CQ messages are enabled or not
|
||||
*/
|
||||
// Timer beaconTimer;
|
||||
beaconTimer = new Timer();
|
||||
beaconTimer.schedule(new BeaconTask(this, this), 10000,
|
||||
this.getChatPreferences().getBcn_beaconIntervalInMinutesMainCat() * 60000);
|
||||
// 60000 * intervalInMinutes = IntervalInMillis
|
||||
scheduleBeaconTimer(INITIAL_BEACON_DELAY_MILLIS);
|
||||
|
||||
/**
|
||||
* The AS querier task will be executed every time but checks for itself whether
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import kst4contest.model.AirPlane;
|
||||
import kst4contest.model.AirPlaneReflectionInfo;
|
||||
import kst4contest.model.ChatMember;
|
||||
import kst4contest.model.ChatPreferences;
|
||||
|
||||
/**
|
||||
* Resolves variables used in operator messages, shortcuts, snippets and
|
||||
* beacons.
|
||||
*
|
||||
* <p>Global variables only depend on the local station configuration and can
|
||||
* therefore be used in every message context. Station variables additionally
|
||||
* require a selected remote station. Keeping both groups in one resolver
|
||||
* prevents the send field and the beacon tasks from implementing different
|
||||
* replacement rules.</p>
|
||||
*/
|
||||
public final class MessageVariableResolver {
|
||||
|
||||
private static final int SHORT_VALUE_LENGTH = 7;
|
||||
|
||||
private final ChatPreferences chatPreferences;
|
||||
|
||||
/**
|
||||
* Creates a resolver backed by the live application preferences.
|
||||
*
|
||||
* @param chatPreferences preferences that provide current station values
|
||||
*/
|
||||
public MessageVariableResolver(ChatPreferences chatPreferences) {
|
||||
this.chatPreferences = Objects.requireNonNull(chatPreferences, "chatPreferences");
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves variables which do not require a selected remote station.
|
||||
*
|
||||
* <p>The replacement is literal rather than regular-expression based.
|
||||
* Callsigns, locators and frequencies are data, not regular expressions.</p>
|
||||
*
|
||||
* @param template text that may contain variables
|
||||
* @return text with all available global variables resolved, or {@code null}
|
||||
* when the supplied template is {@code null}
|
||||
*/
|
||||
public String resolveGlobalVariables(String template) {
|
||||
if (template == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String primaryFrequency = valueOrEmpty(chatPreferences.getMYQRGFirstCat().getValue());
|
||||
String secondaryFrequency = valueOrEmpty(chatPreferences.getMYQRGSecondCat().getValue());
|
||||
String ownLocator = valueOrEmpty(chatPreferences.getStn_loginLocatorMainCat());
|
||||
|
||||
String resolvedText = template;
|
||||
resolvedText = resolvedText.replace("MYQRGSHORT", abbreviate(primaryFrequency));
|
||||
resolvedText = resolvedText.replace("MYQRG", primaryFrequency);
|
||||
resolvedText = resolvedText.replace("SECONDQRG", secondaryFrequency);
|
||||
resolvedText = resolvedText.replace("MYLOCATORSHORT", abbreviateLocator(ownLocator));
|
||||
resolvedText = resolvedText.replace("MYLOCATOR", ownLocator);
|
||||
resolvedText = resolvedText.replace("MYCALL", valueOrEmpty(chatPreferences.getStn_loginCallSign()));
|
||||
resolvedText = resolvedText.replace("MYQTF", formatHeading(chatPreferences.getActualQTF().getValue().doubleValue()));
|
||||
return resolvedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves global variables and variables derived from a selected station.
|
||||
*
|
||||
* <p>If no station is selected, station-specific placeholders remain visible.
|
||||
* This is intentional: silently removing {@code QRZNAME}, {@code FIRSTAP} or
|
||||
* {@code SECONDAP} could create a plausible-looking but incomplete message.</p>
|
||||
*
|
||||
* @param template text that may contain variables
|
||||
* @param selectedStation currently selected remote station, may be {@code null}
|
||||
* @return resolved message text
|
||||
*/
|
||||
public String resolveForSelectedStation(String template, ChatMember selectedStation) {
|
||||
String resolvedText = resolveGlobalVariables(template);
|
||||
|
||||
if (resolvedText == null || selectedStation == null) {
|
||||
return resolvedText;
|
||||
}
|
||||
|
||||
resolvedText = resolvedText.replace("QRZNAME", resolveStationName(selectedStation));
|
||||
resolvedText = resolvedText.replace("FIRSTAP", resolveFirstAirPlane(selectedStation));
|
||||
resolvedText = resolvedText.replace("SECONDAP", resolveSecondAirPlane(selectedStation));
|
||||
return resolvedText;
|
||||
}
|
||||
|
||||
private String resolveStationName(ChatMember selectedStation) {
|
||||
String stationName = valueOrEmpty(selectedStation.getName()).trim();
|
||||
|
||||
if (!stationName.isEmpty()) {
|
||||
return stationName;
|
||||
}
|
||||
|
||||
return valueOrEmpty(selectedStation.getCallSign());
|
||||
}
|
||||
|
||||
private String resolveFirstAirPlane(ChatMember selectedStation) {
|
||||
List<AirPlane> risingAirPlanes = getRisingAirPlanes(selectedStation);
|
||||
|
||||
if (risingAirPlanes.isEmpty()) {
|
||||
return "no ap available";
|
||||
}
|
||||
|
||||
AirPlane firstAirPlane = risingAirPlanes.get(0);
|
||||
return "a " + firstAirPlane.getPotencialDescriptionAsWord()
|
||||
+ " in " + firstAirPlane.getArrivingDurationMinutes() + " min";
|
||||
}
|
||||
|
||||
private String resolveSecondAirPlane(ChatMember selectedStation) {
|
||||
List<AirPlane> risingAirPlanes = getRisingAirPlanes(selectedStation);
|
||||
|
||||
if (risingAirPlanes.size() < 2) {
|
||||
return "";
|
||||
}
|
||||
|
||||
AirPlane secondAirPlane = risingAirPlanes.get(1);
|
||||
return "Next " + secondAirPlane.getPotencialDescriptionAsWord()
|
||||
+ " in " + secondAirPlane.getArrivingDurationMinutes() + " min";
|
||||
}
|
||||
|
||||
private List<AirPlane> getRisingAirPlanes(ChatMember selectedStation) {
|
||||
AirPlaneReflectionInfo reflectionInfo = selectedStation.getAirPlaneReflectInfo();
|
||||
|
||||
if (reflectionInfo == null || reflectionInfo.getRisingAirplanes() == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return reflectionInfo.getRisingAirplanes();
|
||||
}
|
||||
|
||||
private String abbreviate(String value) {
|
||||
return value.substring(0, Math.min(value.length(), SHORT_VALUE_LENGTH));
|
||||
}
|
||||
|
||||
private String abbreviateLocator(String locator) {
|
||||
return locator.substring(0, Math.min(locator.length(), 4));
|
||||
}
|
||||
|
||||
private String formatHeading(double headingDegrees) {
|
||||
if (!Double.isFinite(headingDegrees)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return BigDecimal.valueOf(headingDegrees).stripTrailingZeros().toPlainString();
|
||||
}
|
||||
|
||||
private String valueOrEmpty(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ import java.util.TimerTask;
|
||||
* The task will be runned out of the singleton ChatController instance in an
|
||||
* intervall as specified by the Chatpreferences-instance (typically as
|
||||
* configured in the xml file.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author prakt
|
||||
*
|
||||
*/
|
||||
@@ -34,22 +34,20 @@ public class ScoreboardUpdateTask extends TimerTask {
|
||||
Thread.currentThread().setName("BeaconTask");
|
||||
|
||||
ChatMessage beaconMSG = new ChatMessage();
|
||||
|
||||
String replaceVariables = this.chatController.getChatPreferences().getBcn_beaconTextMainCat();
|
||||
// replaceVariables = bcn_beaconText;
|
||||
|
||||
replaceVariables = replaceVariables.replaceAll("MYQRG", this.chatController.getChatPreferences().getMYQRGFirstCat().getValue());
|
||||
replaceVariables = replaceVariables.replaceAll("MYCALL", this.chatController.getChatPreferences().getStn_loginCallSign());
|
||||
replaceVariables = replaceVariables.replaceAll("MYLOCATOR", this.chatController.getChatPreferences().getStn_loginLocatorMainCat());
|
||||
replaceVariables = replaceVariables.replaceAll("MYQTF", this.chatController.getChatPreferences().getActualQTF().getValue() + "");
|
||||
|
||||
|
||||
MessageVariableResolver variableResolver =
|
||||
new MessageVariableResolver(this.chatController.getChatPreferences());
|
||||
String replaceVariables = variableResolver.resolveGlobalVariables(
|
||||
this.chatController.getChatPreferences().getBcn_beaconTextMainCat()
|
||||
);
|
||||
|
||||
|
||||
beaconMSG.setMessageText(
|
||||
"MSG|" + this.chatController.getChatPreferences().getLoginChatCategoryMain().getCategoryNumber() + "|0|" + replaceVariables + "|0|");
|
||||
beaconMSG.setMessageDirectedToServer(true);
|
||||
|
||||
|
||||
// System.out.println("########### " + replaceVariables);
|
||||
|
||||
|
||||
if (this.chatController.getChatPreferences().isBcn_beaconsEnabledMainCat() ) {
|
||||
|
||||
System.out.println(new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
|
||||
@@ -58,8 +56,8 @@ public class ScoreboardUpdateTask extends TimerTask {
|
||||
} else {
|
||||
//do nothing, CQ is disabled
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package kst4contest.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import kst4contest.controller.MessageVariableResolver;
|
||||
import kst4contest.model.AirPlane;
|
||||
import kst4contest.model.AirPlaneReflectionInfo;
|
||||
import kst4contest.model.ChatMember;
|
||||
import kst4contest.model.ChatPreferences;
|
||||
|
||||
class MessageVariableResolverTest {
|
||||
|
||||
private ChatPreferences chatPreferences;
|
||||
private MessageVariableResolver resolver;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
chatPreferences = mock(ChatPreferences.class);
|
||||
|
||||
when(chatPreferences.getMYQRGFirstCat()).thenReturn(new SimpleStringProperty("144.388.03"));
|
||||
when(chatPreferences.getMYQRGSecondCat()).thenReturn(new SimpleStringProperty("432.088.00"));
|
||||
when(chatPreferences.getStn_loginLocatorMainCat()).thenReturn("JO51IJ");
|
||||
when(chatPreferences.getStn_loginCallSign()).thenReturn("DO5AMF");
|
||||
when(chatPreferences.getActualQTF()).thenReturn(new SimpleDoubleProperty(135.0));
|
||||
|
||||
resolver = new MessageVariableResolver(chatPreferences);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvesAllGlobalVariablesInEveryMessageContext() {
|
||||
String template = "MYCALL MYQRGSHORT MYQRG SECONDQRG MYLOCATORSHORT MYLOCATOR MYQTF";
|
||||
|
||||
assertEquals(
|
||||
"DO5AMF 144.388 144.388.03 432.088.00 JO51 JO51IJ 135",
|
||||
resolver.resolveGlobalVariables(template)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shortValuesRemainSafeWhenTheConfiguredValueIsShorterThanExpected() {
|
||||
when(chatPreferences.getMYQRGFirstCat()).thenReturn(new SimpleStringProperty("144"));
|
||||
when(chatPreferences.getStn_loginLocatorMainCat()).thenReturn("JO5");
|
||||
|
||||
assertEquals("144 JO5", resolver.resolveGlobalVariables("MYQRGSHORT MYLOCATORSHORT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvesSelectedStationNameAndTheFirstTwoAirPlanes() {
|
||||
ChatMember selectedStation = new ChatMember();
|
||||
selectedStation.setCallSign("DL0TEST");
|
||||
selectedStation.setName("Test Operator");
|
||||
|
||||
AirPlane firstAirPlane = new AirPlane();
|
||||
firstAirPlane.setPotential(100);
|
||||
firstAirPlane.setArrivingDurationMinutes(1);
|
||||
|
||||
AirPlane secondAirPlane = new AirPlane();
|
||||
secondAirPlane.setPotential(75);
|
||||
secondAirPlane.setArrivingDurationMinutes(9);
|
||||
|
||||
AirPlaneReflectionInfo reflectionInfo = new AirPlaneReflectionInfo();
|
||||
reflectionInfo.setRisingAirplanes(FXCollections.observableArrayList(firstAirPlane, secondAirPlane));
|
||||
selectedStation.setAirPlaneReflectInfo(reflectionInfo);
|
||||
|
||||
assertEquals(
|
||||
"Hi Test Operator, a very big AP in 1 min; Next big AP in 9 min",
|
||||
resolver.resolveForSelectedStation(
|
||||
"Hi QRZNAME, FIRSTAP; SECONDAP",
|
||||
selectedStation
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesCallsignWhenTheSelectedStationHasNoName() {
|
||||
ChatMember selectedStation = new ChatMember();
|
||||
selectedStation.setCallSign("DL0TEST");
|
||||
selectedStation.setName(" ");
|
||||
|
||||
assertEquals(
|
||||
"Hi DL0TEST",
|
||||
resolver.resolveForSelectedStation("Hi QRZNAME", selectedStation)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsStationVariablesVisibleWhenNoStationIsSelected() {
|
||||
assertEquals(
|
||||
"QRZNAME FIRSTAP SECONDAP",
|
||||
resolver.resolveForSelectedStation("QRZNAME FIRSTAP SECONDAP", null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsUsefulFallbacksWhenNoAirPlaneIsAvailable() {
|
||||
ChatMember selectedStation = new ChatMember();
|
||||
selectedStation.setCallSign("DL0TEST");
|
||||
|
||||
assertEquals(
|
||||
"no ap available ",
|
||||
resolver.resolveForSelectedStation("FIRSTAP SECONDAP", selectedStation)
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,8 @@ module praktiKST {
|
||||
requires java.net.http;
|
||||
requires java.desktop;
|
||||
requires jdk.crypto.ec;
|
||||
requires org.junit.jupiter.api;
|
||||
requires org.mockito;
|
||||
exports kst4contest.controller.interfaces;
|
||||
exports kst4contest.controller;
|
||||
exports kst4contest.locatorUtils;
|
||||
|
||||
Reference in New Issue
Block a user