diff --git a/app/queue-server/edit-control/pom.xml b/app/queue-server/edit-control/pom.xml
new file mode 100644
index 0000000000..2173bd29a1
--- /dev/null
+++ b/app/queue-server/edit-control/pom.xml
@@ -0,0 +1,31 @@
+
+ 4.0.0
+
+ org.phoebus
+ app-queue-server
+ 5.0.3-SNAPSHOT
+
+ app-queue-server-edit-control
+ ${project.groupId}:${project.artifactId}
+
+
+
+ org.phoebus
+ app-queue-server-network
+ ${project.version}
+
+
+ org.phoebus
+ core-framework
+ ${project.version}
+
+
+ org.phoebus
+ core-ui
+ ${project.version}
+
+
+
diff --git a/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlApp.java b/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlApp.java
new file mode 100644
index 0000000000..e471c9c19f
--- /dev/null
+++ b/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlApp.java
@@ -0,0 +1,88 @@
+package org.phoebus.applications.queueserver.editcontrol;
+
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.phoebus.applications.queueserver.Preferences;
+import org.phoebus.applications.queueserver.client.RunEngineHttpClient;
+import org.phoebus.applications.queueserver.util.AppLifecycle;
+import org.phoebus.applications.queueserver.util.PythonParameterConverter;
+import org.phoebus.applications.queueserver.view.ViewFactory;
+import javafx.scene.Parent;
+
+import org.phoebus.framework.spi.AppInstance;
+import org.phoebus.framework.spi.AppResourceDescriptor;
+import org.phoebus.framework.workbench.ApplicationService;
+import org.phoebus.ui.docking.DockItem;
+import org.phoebus.ui.docking.DockPane;
+
+@SuppressWarnings("nls")
+public final class QueueEditControlApp implements AppResourceDescriptor {
+
+ public static final Logger logger = Logger.getLogger(QueueEditControlApp.class.getPackageName());
+
+ public static final String NAME = "queue_edit_control";
+ private static final String DISPLAY_NAME = "Edit & Control Queue";
+
+ static {
+ PythonParameterConverter.initializeInBackground();
+ }
+
+ @Override public String getName() { return NAME; }
+ @Override public String getDisplayName() { return DISPLAY_NAME; }
+
+ @Override public URL getIconURL() {
+ return getClass().getResource("/icons/bluesky.png");
+ }
+
+ @Override public AppInstance create() {
+ initializeHttpClient();
+ AppLifecycle.registerApp();
+
+ Parent root = ViewFactory.EDIT_AND_CONTROL_QUEUE.get();
+ QueueEditControlInstance inst = new QueueEditControlInstance(this, root);
+
+ DockItem tab = new DockItem(inst, root);
+ tab.addClosedNotification(AppLifecycle::unregisterApp);
+ DockPane.getActiveDockPane().addTab(tab);
+
+ return inst;
+ }
+
+ @Override public AppInstance create(java.net.URI resource) {
+ return ApplicationService.createInstance(NAME);
+ }
+
+ private static void initializeHttpClient() {
+ String serverUrl = Preferences.queue_server_url;
+ if (serverUrl == null || serverUrl.trim().isEmpty() || serverUrl.startsWith("$(")) {
+ serverUrl = "http://localhost:60610";
+ logger.log(Level.INFO, "Using default Queue Server URL: " + serverUrl);
+ }
+ String apiKey = resolveApiKey();
+ RunEngineHttpClient.initialize(serverUrl, apiKey);
+ }
+
+ private static String resolveApiKey() {
+ String apiKey = Preferences.api_key;
+ if (apiKey != null && !apiKey.trim().isEmpty() && !apiKey.startsWith("$(")) {
+ return apiKey.trim();
+ }
+ String keyFilePath = Preferences.api_key_file;
+ if (keyFilePath != null && !keyFilePath.trim().isEmpty() && !keyFilePath.startsWith("$(")) {
+ try {
+ Path path = Paths.get(keyFilePath.trim());
+ if (Files.exists(path)) {
+ return Files.readString(path).trim();
+ }
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Failed to read API key from file: " + keyFilePath, e);
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlInstance.java b/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlInstance.java
new file mode 100644
index 0000000000..9a1c2158cc
--- /dev/null
+++ b/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlInstance.java
@@ -0,0 +1,23 @@
+package org.phoebus.applications.queueserver.editcontrol;
+
+import javafx.scene.Node;
+import org.phoebus.framework.persistence.Memento;
+import org.phoebus.framework.spi.AppDescriptor;
+import org.phoebus.framework.spi.AppInstance;
+
+final class QueueEditControlInstance implements AppInstance {
+
+ private final AppDescriptor desc;
+ private final Node view;
+
+ QueueEditControlInstance(AppDescriptor desc, Node view) {
+ this.desc = desc;
+ this.view = view;
+ }
+
+ @Override public AppDescriptor getAppDescriptor() { return desc; }
+ public Node create() { return view; }
+
+ @Override public void restore(Memento m) { /* nothing */ }
+ @Override public void save (Memento m) { /* nothing */ }
+}
diff --git a/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlMenuEntry.java b/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlMenuEntry.java
new file mode 100644
index 0000000000..c911cfeebb
--- /dev/null
+++ b/app/queue-server/edit-control/src/main/java/org/phoebus/applications/queueserver/editcontrol/QueueEditControlMenuEntry.java
@@ -0,0 +1,20 @@
+package org.phoebus.applications.queueserver.editcontrol;
+
+import org.phoebus.applications.queueserver.Messages;
+import org.phoebus.framework.workbench.ApplicationService;
+import org.phoebus.ui.javafx.ImageCache;
+import org.phoebus.ui.spi.MenuEntry;
+import javafx.scene.image.Image;
+
+public final class QueueEditControlMenuEntry implements MenuEntry {
+
+ @Override public String getName() { return Messages.EditControlQueue; }
+ @Override public Image getIcon() { return ImageCache.getImage(
+ QueueEditControlApp.class, "/icons/bluesky.png"); }
+ @Override public String getMenuPath() { return Messages.EditControlQueueMenuPath; }
+
+ @Override public Void call() throws Exception {
+ ApplicationService.createInstance(QueueEditControlApp.NAME);
+ return null;
+ }
+}
diff --git a/app/queue-server/edit-control/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor b/app/queue-server/edit-control/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor
new file mode 100644
index 0000000000..f32744d870
--- /dev/null
+++ b/app/queue-server/edit-control/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor
@@ -0,0 +1 @@
+org.phoebus.applications.queueserver.editcontrol.QueueEditControlApp
diff --git a/app/queue-server/edit-control/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry b/app/queue-server/edit-control/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry
new file mode 100644
index 0000000000..231f980f59
--- /dev/null
+++ b/app/queue-server/edit-control/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry
@@ -0,0 +1 @@
+org.phoebus.applications.queueserver.editcontrol.QueueEditControlMenuEntry
diff --git a/app/queue-server/monitor/pom.xml b/app/queue-server/monitor/pom.xml
new file mode 100644
index 0000000000..96323c6d6e
--- /dev/null
+++ b/app/queue-server/monitor/pom.xml
@@ -0,0 +1,31 @@
+
+ 4.0.0
+
+ org.phoebus
+ app-queue-server
+ 5.0.3-SNAPSHOT
+
+ app-queue-server-monitor
+ ${project.groupId}:${project.artifactId}
+
+
+
+ org.phoebus
+ app-queue-server-network
+ ${project.version}
+
+
+ org.phoebus
+ core-framework
+ ${project.version}
+
+
+ org.phoebus
+ core-ui
+ ${project.version}
+
+
+
diff --git a/app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorApp.java b/app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorApp.java
new file mode 100644
index 0000000000..1fab4629d5
--- /dev/null
+++ b/app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorApp.java
@@ -0,0 +1,88 @@
+package org.phoebus.applications.queueserver.monitor;
+
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.phoebus.applications.queueserver.Preferences;
+import org.phoebus.applications.queueserver.client.RunEngineHttpClient;
+import org.phoebus.applications.queueserver.util.AppLifecycle;
+import org.phoebus.applications.queueserver.util.PythonParameterConverter;
+import org.phoebus.applications.queueserver.view.ViewFactory;
+import javafx.scene.Parent;
+
+import org.phoebus.framework.spi.AppInstance;
+import org.phoebus.framework.spi.AppResourceDescriptor;
+import org.phoebus.framework.workbench.ApplicationService;
+import org.phoebus.ui.docking.DockItem;
+import org.phoebus.ui.docking.DockPane;
+
+@SuppressWarnings("nls")
+public final class QueueMonitorApp implements AppResourceDescriptor {
+
+ public static final Logger logger = Logger.getLogger(QueueMonitorApp.class.getPackageName());
+
+ public static final String NAME = "queue_monitor";
+ private static final String DISPLAY_NAME = "Queue Monitor";
+
+ static {
+ PythonParameterConverter.initializeInBackground();
+ }
+
+ @Override public String getName() { return NAME; }
+ @Override public String getDisplayName() { return DISPLAY_NAME; }
+
+ @Override public URL getIconURL() {
+ return getClass().getResource("/icons/bluesky.png");
+ }
+
+ @Override public AppInstance create() {
+ initializeHttpClient();
+ AppLifecycle.registerApp();
+
+ Parent root = ViewFactory.MONITOR_QUEUE.get();
+ QueueMonitorInstance inst = new QueueMonitorInstance(this, root);
+
+ DockItem tab = new DockItem(inst, root);
+ tab.addClosedNotification(AppLifecycle::unregisterApp);
+ DockPane.getActiveDockPane().addTab(tab);
+
+ return inst;
+ }
+
+ @Override public AppInstance create(java.net.URI resource) {
+ return ApplicationService.createInstance(NAME);
+ }
+
+ private static void initializeHttpClient() {
+ String serverUrl = Preferences.queue_server_url;
+ if (serverUrl == null || serverUrl.trim().isEmpty() || serverUrl.startsWith("$(")) {
+ serverUrl = "http://localhost:60610";
+ logger.log(Level.INFO, "Using default Queue Server URL: " + serverUrl);
+ }
+ String apiKey = resolveApiKey();
+ RunEngineHttpClient.initialize(serverUrl, apiKey);
+ }
+
+ private static String resolveApiKey() {
+ String apiKey = Preferences.api_key;
+ if (apiKey != null && !apiKey.trim().isEmpty() && !apiKey.startsWith("$(")) {
+ return apiKey.trim();
+ }
+ String keyFilePath = Preferences.api_key_file;
+ if (keyFilePath != null && !keyFilePath.trim().isEmpty() && !keyFilePath.startsWith("$(")) {
+ try {
+ Path path = Paths.get(keyFilePath.trim());
+ if (Files.exists(path)) {
+ return Files.readString(path).trim();
+ }
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Failed to read API key from file: " + keyFilePath, e);
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerInstance.java b/app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorInstance.java
similarity index 76%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerInstance.java
rename to app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorInstance.java
index b522eea5c1..f7ab5aa5f6 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerInstance.java
+++ b/app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorInstance.java
@@ -1,16 +1,16 @@
-package org.phoebus.applications.queueserver;
+package org.phoebus.applications.queueserver.monitor;
import javafx.scene.Node;
import org.phoebus.framework.persistence.Memento;
import org.phoebus.framework.spi.AppDescriptor;
import org.phoebus.framework.spi.AppInstance;
-final class QueueServerInstance implements AppInstance {
+final class QueueMonitorInstance implements AppInstance {
private final AppDescriptor desc;
private final Node view;
- QueueServerInstance(AppDescriptor desc, Node view) {
+ QueueMonitorInstance(AppDescriptor desc, Node view) {
this.desc = desc;
this.view = view;
}
@@ -20,4 +20,4 @@ final class QueueServerInstance implements AppInstance {
@Override public void restore(Memento m) { /* nothing */ }
@Override public void save (Memento m) { /* nothing */ }
-}
\ No newline at end of file
+}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerMenuEntry.java b/app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorMenuEntry.java
similarity index 57%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerMenuEntry.java
rename to app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorMenuEntry.java
index 06f99e5266..63e0859bc2 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerMenuEntry.java
+++ b/app/queue-server/monitor/src/main/java/org/phoebus/applications/queueserver/monitor/QueueMonitorMenuEntry.java
@@ -1,20 +1,20 @@
-package org.phoebus.applications.queueserver;
+package org.phoebus.applications.queueserver.monitor;
+import org.phoebus.applications.queueserver.Messages;
import org.phoebus.framework.workbench.ApplicationService;
import org.phoebus.ui.javafx.ImageCache;
import org.phoebus.ui.spi.MenuEntry;
import javafx.scene.image.Image;
-public final class QueueServerMenuEntry implements MenuEntry {
+public final class QueueMonitorMenuEntry implements MenuEntry {
- @Override public String getName() { return Messages.QueueServer; }
+ @Override public String getName() { return Messages.QueueMonitor; }
@Override public Image getIcon() { return ImageCache.getImage(
- QueueServerApp.class,
- "/icons/bluesky.png"); } // same icon as descriptor
- @Override public String getMenuPath() { return Messages.QueueServerMenuPath; }
+ QueueMonitorApp.class, "/icons/bluesky.png"); }
+ @Override public String getMenuPath() { return Messages.QueueMonitorMenuPath; }
@Override public Void call() throws Exception {
- ApplicationService.createInstance(QueueServerApp.NAME);
+ ApplicationService.createInstance(QueueMonitorApp.NAME);
return null;
}
}
diff --git a/app/queue-server/monitor/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor b/app/queue-server/monitor/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor
new file mode 100644
index 0000000000..9f6da18f43
--- /dev/null
+++ b/app/queue-server/monitor/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor
@@ -0,0 +1 @@
+org.phoebus.applications.queueserver.monitor.QueueMonitorApp
diff --git a/app/queue-server/monitor/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry b/app/queue-server/monitor/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry
new file mode 100644
index 0000000000..e080c12294
--- /dev/null
+++ b/app/queue-server/monitor/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry
@@ -0,0 +1 @@
+org.phoebus.applications.queueserver.monitor.QueueMonitorMenuEntry
diff --git a/app/queue-server/network/pom.xml b/app/queue-server/network/pom.xml
new file mode 100644
index 0000000000..7e1294b774
--- /dev/null
+++ b/app/queue-server/network/pom.xml
@@ -0,0 +1,73 @@
+
+ 4.0.0
+
+ org.phoebus
+ app-queue-server
+ 5.0.3-SNAPSHOT
+
+ app-queue-server-network
+ ${project.groupId}:${project.artifactId}
+
+
+
+ org.phoebus
+ core-framework
+ ${project.version}
+
+
+ org.phoebus
+ core-ui
+ ${project.version}
+
+
+ org.phoebus
+ core-types
+ ${project.version}
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ 2.19.1
+
+
+ org.python
+ jython-standalone
+ ${jython.version}
+
+
+ org.apache.poi
+ poi
+
+
+ com.zaxxer
+ SparseBitSet
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ jcl-over-slf4j
+
+
+ commons-codec
+ commons-codec
+
+
+ org.apache.commons
+ commons-collections4
+
+
+ org.apache.commons
+ commons-math3
+
+
+ 5.0.0
+ compile
+
+
+
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/Messages.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/Messages.java
similarity index 60%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/Messages.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/Messages.java
index 25fe16fb50..91c76efff5 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/Messages.java
+++ b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/Messages.java
@@ -2,13 +2,15 @@
import org.phoebus.framework.nls.NLS;
-/** Externalised strings for Queue-Monitor plug-in */
+/** Externalised strings for Queue Server plug-ins */
@SuppressWarnings("nls")
public final class Messages {
// ---------- keep alphabetically sorted ----------
- public static String QueueServer; // display name
- public static String QueueServerMenuPath; // menu path
+ public static String EditControlQueue;
+ public static String EditControlQueueMenuPath;
+ public static String QueueMonitor;
+ public static String QueueMonitorMenuPath;
// -----------------------------------------------
static { NLS.initializeMessages(Messages.class); }
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/Preferences.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/Preferences.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/Preferences.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/Preferences.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputText.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputText.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputText.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputText.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputUid.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputUid.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputUid.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputUid.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputUpdate.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputUpdate.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputUpdate.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputUpdate.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputWsMessage.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputWsMessage.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputWsMessage.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/ConsoleOutputWsMessage.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/Envelope.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/Envelope.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/Envelope.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/Envelope.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/EverythingElse.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/EverythingElse.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/EverythingElse.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/EverythingElse.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/HistoryGetPayload.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/HistoryGetPayload.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/HistoryGetPayload.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/HistoryGetPayload.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/NoBody.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/NoBody.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/NoBody.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/NoBody.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueGetPayload.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueGetPayload.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueGetPayload.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueGetPayload.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItem.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItem.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItem.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItem.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItemAdd.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItemAdd.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItemAdd.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItemAdd.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItemAddBatch.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItemAddBatch.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItemAddBatch.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItemAddBatch.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItemMove.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItemMove.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItemMove.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItemMove.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItemMoveBatch.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItemMoveBatch.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/QueueItemMoveBatch.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/QueueItemMoveBatch.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/StatusResponse.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/StatusResponse.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/StatusResponse.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/StatusResponse.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/StatusWsMessage.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/StatusWsMessage.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/StatusWsMessage.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/StatusWsMessage.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/SystemInfoWsMessage.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/SystemInfoWsMessage.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/api/SystemInfoWsMessage.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/api/SystemInfoWsMessage.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/ApiEndpoint.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/ApiEndpoint.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/ApiEndpoint.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/ApiEndpoint.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/Endpoint.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/Endpoint.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/Endpoint.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/Endpoint.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/HttpMethod.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/HttpMethod.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/HttpMethod.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/HttpMethod.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/QueueServerWebSocket.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/QueueServerWebSocket.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/QueueServerWebSocket.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/QueueServerWebSocket.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineHttpClient.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/RunEngineHttpClient.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineHttpClient.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/RunEngineHttpClient.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineService.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/RunEngineService.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineService.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/client/RunEngineService.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/EditAndControlQueueController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/EditAndControlQueueController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/EditAndControlQueueController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/EditAndControlQueueController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/MonitorQueueController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/MonitorQueueController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/MonitorQueueController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/MonitorQueueController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReConsoleMonitorController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReConsoleMonitorController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReConsoleMonitorController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReConsoleMonitorController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReEnvironmentControlsController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReEnvironmentControlsController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReEnvironmentControlsController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReEnvironmentControlsController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReExecutionControlsController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReExecutionControlsController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReExecutionControlsController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReExecutionControlsController.java
diff --git a/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReManagerConnectionController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReManagerConnectionController.java
new file mode 100644
index 0000000000..f6b96a55b3
--- /dev/null
+++ b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReManagerConnectionController.java
@@ -0,0 +1,78 @@
+package org.phoebus.applications.queueserver.controller;
+
+import org.phoebus.applications.queueserver.util.ConnectionManager;
+import org.phoebus.applications.queueserver.util.ConnectionManager.ConnectionState;
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToggleButton;
+
+/**
+ * Thin UI controller for the connection manager widget.
+ * All connection logic lives in {@link ConnectionManager}.
+ * Multiple instances of this controller (one per app) share the same
+ * ConnectionManager singleton, so connecting in one app connects both.
+ */
+public final class ReManagerConnectionController {
+
+ @FXML private ToggleButton autoConnectToggle;
+ @FXML private Label connectionStatusLabel;
+
+ private final ConnectionManager cm = ConnectionManager.getInstance();
+
+ @FXML
+ public void initialize() {
+ // Reflect current state immediately
+ updateUI(cm.getState());
+ autoConnectToggle.setSelected(cm.isAutoConnect());
+
+ // Listen for shared state changes
+ cm.stateProperty().addListener((obs, oldState, newState) -> updateUI(newState));
+
+ // Auto-connect on startup if toggle is selected and not yet connected
+ if (autoConnectToggle.isSelected()
+ && cm.getState() == ConnectionState.DISCONNECTED) {
+ cm.start();
+ }
+ }
+
+ @FXML
+ private void toggleConnection() {
+ if (autoConnectToggle.isSelected()) {
+ cm.start();
+ } else {
+ cm.stop();
+ }
+ }
+
+ private void updateUI(ConnectionState state) {
+ autoConnectToggle.setSelected(state != ConnectionState.DISCONNECTED);
+
+ switch (state) {
+ case DISCONNECTED -> {
+ connectionStatusLabel.setText("OFFLINE");
+ connectionStatusLabel.setStyle("-fx-text-fill: grey;");
+ autoConnectToggle.setText("Connect");
+ }
+ case CONNECTING -> {
+ connectionStatusLabel.setText("CONNECTING");
+ connectionStatusLabel.setStyle("-fx-text-fill: grey;");
+ autoConnectToggle.setText("Disconnect");
+ }
+ case NETWORK_ERROR -> {
+ connectionStatusLabel.setText("NETWORK");
+ connectionStatusLabel.setStyle("-fx-text-fill: #00008B;");
+ autoConnectToggle.setText("Disconnect");
+ }
+ case NO_STATUS -> {
+ connectionStatusLabel.setText("STATUS");
+ connectionStatusLabel.setStyle("-fx-text-fill: red;");
+ autoConnectToggle.setText("Disconnect");
+ }
+ case CONNECTED -> {
+ connectionStatusLabel.setText("CONNECTED");
+ connectionStatusLabel.setStyle("-fx-text-fill: green;");
+ autoConnectToggle.setText("Disconnect");
+ }
+ }
+ }
+}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanEditorController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanEditorController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanEditorController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanEditorController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanHistoryController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanHistoryController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanHistoryController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanHistoryController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanManagerController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanManagerController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanManagerController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanManagerController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanQueueController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanQueueController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanQueueController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanQueueController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanViewerController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanViewerController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanViewerController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/RePlanViewerController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReQueueControlsController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReQueueControlsController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReQueueControlsController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReQueueControlsController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReRunningPlanController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReRunningPlanController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReRunningPlanController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReRunningPlanController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReStatusMonitorController.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReStatusMonitorController.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReStatusMonitorController.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/controller/ReStatusMonitorController.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/AppLifecycle.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/AppLifecycle.java
similarity index 54%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/AppLifecycle.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/AppLifecycle.java
index 0c6e27e61c..8545cd87d7 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/AppLifecycle.java
+++ b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/AppLifecycle.java
@@ -4,38 +4,52 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * Manages application lifecycle - especially cleanup when the app is closed
- * so that reopening within Phoebus gets a fresh state.
+ * Manages application lifecycle with reference counting.
+ * Multiple queue-server apps (monitor, edit-control) can be open simultaneously.
+ * Shared state is only reset when the last app is closed.
*/
public final class AppLifecycle {
private static final Logger logger = Logger.getLogger(AppLifecycle.class.getPackageName());
-
private static final List shutdownCallbacks = new ArrayList<>();
+ private static final AtomicInteger activeApps = new AtomicInteger(0);
private AppLifecycle() {}
+ /** Called when a queue-server app is opened. */
+ public static void registerApp() {
+ int count = activeApps.incrementAndGet();
+ logger.log(Level.FINE, "App registered, active count: " + count);
+ }
+
/**
- * Register a shutdown callback.
- * Called by controllers during initialization.
+ * Called when a queue-server app tab is closed.
+ * Shared state is reset only when the last app closes.
*/
+ public static void unregisterApp() {
+ int count = activeApps.decrementAndGet();
+ logger.log(Level.FINE, "App unregistered, active count: " + count);
+ if (count <= 0) {
+ activeApps.set(0);
+ shutdown();
+ }
+ }
+
+ /** Register a shutdown callback. */
public static void registerShutdown(Runnable shutdown) {
shutdownCallbacks.add(shutdown);
}
- /**
- * Reset all static state for app restart.
- * Called by QueueServerInstance when the app tab is closed.
- */
- public static void shutdown() {
+ /** Reset all static state. Called when the last app closes. */
+ private static void shutdown() {
try {
- logger.log(Level.INFO, "Queue Server app shutting down - resetting state");
+ logger.log(Level.INFO, "Last queue-server app closed - resetting state");
- // Run all registered shutdown callbacks
for (Runnable callback : shutdownCallbacks) {
try {
callback.run();
@@ -45,7 +59,10 @@ public static void shutdown() {
}
shutdownCallbacks.clear();
- // Reset all event buses - these just clear listeners, no callbacks fired
+ // Shut down connection manager
+ try { ConnectionManager.getInstance().shutdown(); } catch (Exception e) { /* ignore */ }
+
+ // Reset all event buses
try { StatusBus.reset(); } catch (Exception e) { /* ignore */ }
try { QueueItemSelectionEvent.getInstance().reset(); } catch (Exception e) { /* ignore */ }
try { ItemUpdateEvent.getInstance().reset(); } catch (Exception e) { /* ignore */ }
@@ -54,10 +71,10 @@ public static void shutdown() {
try { UiSignalEvent.reset(); } catch (Exception e) { /* ignore */ }
try { PlansCache.reset(); } catch (Exception e) { /* ignore */ }
- // Reset Python converter so it can be re-initialized on next app open
+ // Reset Python converter
try { PythonParameterConverter.resetShared(); } catch (Exception e) { /* ignore */ }
- logger.log(Level.INFO, "Queue Server app state reset complete");
+ logger.log(Level.INFO, "Queue Server state reset complete");
} catch (Exception e) {
logger.log(Level.WARNING, "Error during app shutdown", e);
}
diff --git a/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/ConnectionManager.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/ConnectionManager.java
new file mode 100644
index 0000000000..f8a013f159
--- /dev/null
+++ b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/ConnectionManager.java
@@ -0,0 +1,301 @@
+package org.phoebus.applications.queueserver.util;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import javafx.application.Platform;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import org.phoebus.applications.queueserver.Preferences;
+import org.phoebus.applications.queueserver.api.StatusResponse;
+import org.phoebus.applications.queueserver.api.StatusWsMessage;
+import org.phoebus.applications.queueserver.client.QueueServerWebSocket;
+import org.phoebus.applications.queueserver.client.RunEngineService;
+
+import java.util.Map;
+import java.util.concurrent.ScheduledFuture;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Singleton that owns the queue-server connection (WebSocket or HTTP polling).
+ * Multiple UI controllers ({@link org.phoebus.applications.queueserver.controller.ReManagerConnectionController})
+ * bind to the shared {@link #stateProperty()} and call {@link #start()}/{@link #stop()}.
+ */
+public final class ConnectionManager {
+
+ private static final ConnectionManager INSTANCE = new ConnectionManager();
+ private static final Logger logger = Logger.getLogger(ConnectionManager.class.getPackageName());
+
+ private final RunEngineService svc = new RunEngineService();
+ private final ObjectMapper mapper = new ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ private ScheduledFuture> pollTask;
+ private ScheduledFuture> reconnectTask;
+ private ScheduledFuture> healthCheckTask;
+ private QueueServerWebSocket statusWs;
+
+ private volatile StatusResponse latestStatus = null;
+ private volatile boolean websocketConnected = false;
+ private volatile boolean attemptingConnection = false;
+ private volatile long lastStatusUpdateTime = 0;
+ private volatile long connectionAttemptStartTime = 0;
+ private volatile int reconnectAttempts = 0;
+
+ private static final long STATUS_TIMEOUT_MS = 3000;
+ private static final long CONNECTION_ATTEMPT_TIMEOUT_MS = 5000;
+ private static final long RECONNECT_INTERVAL_MS = 5000;
+
+ public enum ConnectionState {
+ DISCONNECTED,
+ CONNECTING,
+ NETWORK_ERROR,
+ NO_STATUS,
+ CONNECTED
+ }
+
+ /** Observable state – always updated on the FX thread. */
+ private final ObjectProperty state =
+ new SimpleObjectProperty<>(ConnectionState.DISCONNECTED);
+
+ private volatile boolean autoConnect = false;
+
+ private ConnectionManager() {}
+
+ public static ConnectionManager getInstance() { return INSTANCE; }
+
+ public ObjectProperty stateProperty() { return state; }
+
+ public ConnectionState getState() { return state.get(); }
+
+ public boolean isAutoConnect() { return autoConnect; }
+
+ /** Idempotent – does nothing if already running. */
+ public void start() {
+ if (autoConnect) return;
+ autoConnect = true;
+
+ if (pollTask != null && !pollTask.isDone()) return;
+ if (statusWs != null && statusWs.isConnected()) return;
+
+ websocketConnected = false;
+ attemptingConnection = false;
+ latestStatus = null;
+ lastStatusUpdateTime = 0;
+ connectionAttemptStartTime = 0;
+ reconnectAttempts = 0;
+
+ if (Preferences.use_websockets) {
+ logger.log(Level.INFO, "Starting status WebSocket connection");
+ attemptWebSocketConnection();
+ } else {
+ logger.log(Level.INFO, "Starting status polling every " + Preferences.update_interval_ms + " ms");
+ attemptingConnection = true;
+ connectionAttemptStartTime = System.currentTimeMillis();
+ pollTask = PollCenter.everyMs(Preferences.update_interval_ms,
+ this::queryStatusOnce,
+ this::updateWidgets);
+ }
+
+ if (healthCheckTask == null) {
+ healthCheckTask = PollCenter.everyMs(500, this::checkConnectionHealth);
+ }
+ }
+
+ /** Idempotent – does nothing if already stopped. */
+ public void stop() {
+ logger.log(Level.INFO, "Stopping status monitoring");
+ autoConnect = false;
+
+ if (reconnectTask != null) { reconnectTask.cancel(true); reconnectTask = null; }
+ if (healthCheckTask != null) { healthCheckTask.cancel(true); healthCheckTask = null; }
+ if (pollTask != null) { pollTask.cancel(true); pollTask = null; }
+ if (statusWs != null) { statusWs.disconnect(); statusWs = null; }
+
+ websocketConnected = false;
+ reconnectAttempts = 0;
+
+ Platform.runLater(() -> {
+ state.set(ConnectionState.DISCONNECTED);
+ latestStatus = null;
+ StatusBus.push(null);
+ });
+ }
+
+ /** Full shutdown – resets everything. Called by AppLifecycle. */
+ public void shutdown() {
+ stop();
+ }
+
+ // ---- WebSocket connection -------------------------------------------
+
+ private void attemptWebSocketConnection() {
+ try {
+ attemptingConnection = true;
+ connectionAttemptStartTime = System.currentTimeMillis();
+
+ statusWs = svc.createStatusWebSocket();
+ statusWs.addListener(msg -> {
+ Map statusMap = msg.status();
+ if (statusMap != null) {
+ try {
+ latestStatus = mapper.convertValue(statusMap, StatusResponse.class);
+ lastStatusUpdateTime = System.currentTimeMillis();
+ if (!websocketConnected) {
+ websocketConnected = true;
+ attemptingConnection = false;
+ reconnectAttempts = 0;
+ logger.log(Level.INFO, "WebSocket connection established");
+ }
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Failed to parse status from WebSocket", e);
+ }
+ }
+ });
+
+ statusWs.connect();
+
+ pollTask = PollCenter.everyMs(Preferences.update_interval_ms, () -> {
+ if (statusWs != null && !statusWs.isConnected()) {
+ if (websocketConnected) {
+ logger.log(Level.WARNING, "WebSocket connection lost");
+ websocketConnected = false;
+ attemptingConnection = false;
+ if (autoConnect) scheduleReconnect();
+ }
+ }
+ StatusResponse status = latestStatus;
+ if (status != null) {
+ Platform.runLater(() -> updateWidgets(status));
+ }
+ });
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to create WebSocket connection", e);
+ websocketConnected = false;
+ attemptingConnection = false;
+ if (autoConnect) scheduleReconnect();
+ }
+ }
+
+ private void scheduleReconnect() {
+ if (!autoConnect) return;
+ if (reconnectTask != null && !reconnectTask.isDone()) return;
+ if (attemptingConnection) return;
+ if (reconnectTask != null) reconnectTask.cancel(false);
+
+ reconnectAttempts++;
+ logger.log(Level.INFO, "Scheduling reconnect attempt #" + reconnectAttempts
+ + " in " + RECONNECT_INTERVAL_MS + "ms");
+
+ reconnectTask = PollCenter.afterMs((int) RECONNECT_INTERVAL_MS, () -> {
+ if (autoConnect && !attemptingConnection) {
+ logger.log(Level.INFO, "Attempting reconnect (attempt #" + reconnectAttempts + ")");
+ if (statusWs != null) { statusWs.disconnect(); statusWs = null; }
+ if (pollTask != null) { pollTask.cancel(false); pollTask = null; }
+ websocketConnected = false;
+ attemptWebSocketConnection();
+ }
+ });
+ }
+
+ // ---- HTTP polling ---------------------------------------------------
+
+ private StatusResponse queryStatusOnce() {
+ try {
+ StatusResponse status = svc.status();
+ if (status != null) {
+ lastStatusUpdateTime = System.currentTimeMillis();
+ if (attemptingConnection) {
+ attemptingConnection = false;
+ reconnectAttempts = 0;
+ logger.log(Level.INFO, "HTTP connection established");
+ }
+ }
+ return status;
+ } catch (Exception ex) {
+ logger.log(Level.FINE, "Status query failed: " + ex.getMessage());
+ return null;
+ }
+ }
+
+ // ---- Health check ---------------------------------------------------
+
+ private void checkConnectionHealth() {
+ ConnectionState newState;
+
+ if (!autoConnect) {
+ newState = ConnectionState.DISCONNECTED;
+ } else if (Preferences.use_websockets) {
+ newState = checkWebSocketHealth();
+ } else {
+ newState = checkPollingHealth();
+ }
+
+ if (newState != state.get()) {
+ ConnectionState oldState = state.get();
+ logger.log(Level.FINE, "Connection state: " + oldState + " -> " + newState);
+ Platform.runLater(() -> {
+ state.set(newState);
+ applyStateEffects(newState);
+ });
+ }
+ }
+
+ private ConnectionState checkWebSocketHealth() {
+ if (attemptingConnection) {
+ long elapsed = System.currentTimeMillis() - connectionAttemptStartTime;
+ if (elapsed > CONNECTION_ATTEMPT_TIMEOUT_MS) {
+ attemptingConnection = false;
+ if (pollTask != null) { pollTask.cancel(false); pollTask = null; }
+ if (statusWs != null) { statusWs.disconnect(); statusWs = null; }
+ scheduleReconnect();
+ return ConnectionState.NETWORK_ERROR;
+ }
+ return ConnectionState.CONNECTING;
+ }
+ if (!websocketConnected) {
+ if ((reconnectTask == null || reconnectTask.isDone())
+ && (statusWs == null || !statusWs.isConnected())) {
+ scheduleReconnect();
+ }
+ return ConnectionState.NETWORK_ERROR;
+ }
+ if (lastStatusUpdateTime == 0) {
+ return ConnectionState.CONNECTING;
+ }
+ long age = System.currentTimeMillis() - lastStatusUpdateTime;
+ return age > STATUS_TIMEOUT_MS ? ConnectionState.NO_STATUS : ConnectionState.CONNECTED;
+ }
+
+ private ConnectionState checkPollingHealth() {
+ if (attemptingConnection) {
+ long elapsed = System.currentTimeMillis() - connectionAttemptStartTime;
+ return elapsed > CONNECTION_ATTEMPT_TIMEOUT_MS
+ ? ConnectionState.NETWORK_ERROR
+ : ConnectionState.CONNECTING;
+ }
+ if (lastStatusUpdateTime == 0) return ConnectionState.NETWORK_ERROR;
+ long age = System.currentTimeMillis() - lastStatusUpdateTime;
+ return age > STATUS_TIMEOUT_MS ? ConnectionState.NETWORK_ERROR : ConnectionState.CONNECTED;
+ }
+
+ private void applyStateEffects(ConnectionState s) {
+ switch (s) {
+ case DISCONNECTED, NETWORK_ERROR, NO_STATUS -> {
+ latestStatus = null;
+ StatusBus.push(null);
+ }
+ case CONNECTED -> PlansCache.loadIfNeeded();
+ default -> { }
+ }
+ }
+
+ // ---- Push to StatusBus ----------------------------------------------
+
+ private void updateWidgets(StatusResponse s) {
+ if (s != null) {
+ logger.log(Level.FINEST, "Status update: manager_state=" + s.managerState());
+ StatusBus.push(s);
+ }
+ }
+}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/HttpSupport.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/HttpSupport.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/HttpSupport.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/HttpSupport.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/JythonScriptExecutor.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/JythonScriptExecutor.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/JythonScriptExecutor.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/JythonScriptExecutor.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/PlansCache.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/PlansCache.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/PlansCache.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/PlansCache.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/PollCenter.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/PollCenter.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/PollCenter.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/PollCenter.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/PythonParameterConverter.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/PythonParameterConverter.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/PythonParameterConverter.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/PythonParameterConverter.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/QueueItemSelectionEvent.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/QueueItemSelectionEvent.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/QueueItemSelectionEvent.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/QueueItemSelectionEvent.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/RateLimiter.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/RateLimiter.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/RateLimiter.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/RateLimiter.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/RunEngineCli.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/RunEngineCli.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/RunEngineCli.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/RunEngineCli.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/RunEngineRepl.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/RunEngineRepl.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/RunEngineRepl.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/RunEngineRepl.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/StatusBus.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/StatusBus.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/util/StatusBus.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/util/StatusBus.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/ItemUpdateEvent.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/ItemUpdateEvent.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/ItemUpdateEvent.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/ItemUpdateEvent.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/PlanEditEvent.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/PlanEditEvent.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/PlanEditEvent.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/PlanEditEvent.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/TabSwitchEvent.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/TabSwitchEvent.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/TabSwitchEvent.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/TabSwitchEvent.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/UiSignalEvent.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/UiSignalEvent.java
similarity index 100%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/UiSignalEvent.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/UiSignalEvent.java
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/ViewFactory.java b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/ViewFactory.java
similarity index 92%
rename from app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/ViewFactory.java
rename to app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/ViewFactory.java
index 2c197c34bd..89d4fc4c59 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/view/ViewFactory.java
+++ b/app/queue-server/network/src/main/java/org/phoebus/applications/queueserver/view/ViewFactory.java
@@ -17,7 +17,6 @@
*/
public enum ViewFactory {
- APPLICATION ("/org/phoebus/applications/queueserver/view/Application.fxml"),
MONITOR_QUEUE ("/org/phoebus/applications/queueserver/view/MonitorQueue.fxml"),
EDIT_AND_CONTROL_QUEUE ("/org/phoebus/applications/queueserver/view/EditAndControlQueue.fxml");
diff --git a/app/queue-server/src/main/resources/icons/bluesky.png b/app/queue-server/network/src/main/resources/icons/bluesky.png
similarity index 100%
rename from app/queue-server/src/main/resources/icons/bluesky.png
rename to app/queue-server/network/src/main/resources/icons/bluesky.png
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/css/style.css b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/css/style.css
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/css/style.css
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/css/style.css
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/logging.properties b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/logging.properties
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/logging.properties
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/logging.properties
diff --git a/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/messages.properties b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/messages.properties
new file mode 100644
index 0000000000..4f54486fdb
--- /dev/null
+++ b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/messages.properties
@@ -0,0 +1,4 @@
+EditControlQueue=Edit & Control Queue
+EditControlQueueMenuPath=Utility
+QueueMonitor=Queue Monitor
+QueueMonitorMenuPath=Utility
diff --git a/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/messages_fr.properties b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/messages_fr.properties
new file mode 100644
index 0000000000..2eb7a0f569
--- /dev/null
+++ b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/messages_fr.properties
@@ -0,0 +1,4 @@
+EditControlQueue=Modifier & Contr\u00f4ler la file
+EditControlQueueMenuPath=Outil
+QueueMonitor=Moniteur de file d'attente
+QueueMonitorMenuPath=Outil
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/scripts/type_converter.py b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/scripts/type_converter.py
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/scripts/type_converter.py
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/scripts/type_converter.py
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/EditAndControlQueue.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/EditAndControlQueue.fxml
similarity index 76%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/EditAndControlQueue.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/EditAndControlQueue.fxml
index 5d94bedfed..c7d57525fe 100644
--- a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/EditAndControlQueue.fxml
+++ b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/EditAndControlQueue.fxml
@@ -4,24 +4,29 @@
-
+
-
+
+
+
+
+
+
-
+
-
+
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/MonitorQueue.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/MonitorQueue.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/MonitorQueue.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/MonitorQueue.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReConsoleMonitor.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReConsoleMonitor.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReConsoleMonitor.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReConsoleMonitor.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReEnvironmentControls.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReEnvironmentControls.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReEnvironmentControls.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReEnvironmentControls.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReExecutionControls.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReExecutionControls.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReExecutionControls.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReExecutionControls.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReManagerConnection.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReManagerConnection.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReManagerConnection.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReManagerConnection.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanEditor.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanEditor.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanEditor.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanEditor.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanHistory.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanHistory.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanHistory.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanHistory.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanManager.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanManager.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanManager.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanManager.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanQueue.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanQueue.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanQueue.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanQueue.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanViewer.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanViewer.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/RePlanViewer.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/RePlanViewer.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReQueueControls.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReQueueControls.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReQueueControls.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReQueueControls.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReRunningPlan.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReRunningPlan.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReRunningPlan.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReRunningPlan.fxml
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReStatusMonitor.fxml b/app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReStatusMonitor.fxml
similarity index 100%
rename from app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/ReStatusMonitor.fxml
rename to app/queue-server/network/src/main/resources/org/phoebus/applications/queueserver/view/ReStatusMonitor.fxml
diff --git a/app/queue-server/src/main/resources/queueserver_preferences.properties b/app/queue-server/network/src/main/resources/queueserver_preferences.properties
similarity index 100%
rename from app/queue-server/src/main/resources/queueserver_preferences.properties
rename to app/queue-server/network/src/main/resources/queueserver_preferences.properties
diff --git a/app/queue-server/pom.xml b/app/queue-server/pom.xml
index 1310dac5f3..90d140058e 100644
--- a/app/queue-server/pom.xml
+++ b/app/queue-server/pom.xml
@@ -3,6 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
+ pom
app-queue-server
${project.groupId}:${project.artifactId}
@@ -10,67 +11,9 @@
app
5.0.3-SNAPSHOT
-
-
-
- org.phoebus
- core-framework
- ${project.version}
-
-
- org.phoebus
- core-ui
- ${project.version}
-
-
- org.phoebus
- core-types
- ${project.version}
-
-
-
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-yaml
- 2.19.1
-
-
-
- org.python
- jython-standalone
- ${jython.version}
-
-
- org.apache.poi
- poi
-
-
- com.zaxxer
- SparseBitSet
-
-
- org.slf4j
- slf4j-api
-
-
- org.slf4j
- jcl-over-slf4j
-
-
- commons-codec
- commons-codec
-
-
- org.apache.commons
- commons-collections4
-
-
- org.apache.commons
- commons-math3
-
-
- 5.0.0
- compile
-
-
+
+ network
+ monitor
+ edit-control
+
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerApp.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerApp.java
deleted file mode 100644
index c2f14f2218..0000000000
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/QueueServerApp.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package org.phoebus.applications.queueserver;
-
-import java.io.IOException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.phoebus.applications.queueserver.client.RunEngineHttpClient;
-import org.phoebus.applications.queueserver.util.AppLifecycle;
-import org.phoebus.applications.queueserver.view.ViewFactory;
-import javafx.scene.Parent;
-
-import org.phoebus.framework.spi.AppInstance;
-import org.phoebus.framework.spi.AppResourceDescriptor;
-import org.phoebus.framework.workbench.ApplicationService;
-import org.phoebus.ui.docking.DockItem;
-import org.phoebus.ui.docking.DockPane;
-
-@SuppressWarnings("nls")
-public final class QueueServerApp implements AppResourceDescriptor {
-
- public static final Logger logger = Logger.getLogger(QueueServerApp.class.getPackageName());
-
- public static final String NAME = "queue-server";
- private static final String DISPLAY_NAME = "Queue Server";
-
- // Start Jython warmup as soon as plugin loads (during Phoebus startup)
- // This runs before the user opens the app, reducing/eliminating UI freeze
- static {
- org.phoebus.applications.queueserver.util.PythonParameterConverter.initializeInBackground();
- }
-
- @Override public String getName() { return NAME; }
- @Override public String getDisplayName() { return DISPLAY_NAME; }
-
- @Override public URL getIconURL() {
- return getClass().getResource("/icons/bluesky.png"); // add one or reuse probe.png
- }
-
- @Override public AppInstance create() {
- // Resolve server URL with default fallback
- String serverUrl = Preferences.queue_server_url;
- // Check if the preference wasn't expanded (still has $(VAR) syntax) or is empty
- if (serverUrl == null || serverUrl.trim().isEmpty() || serverUrl.startsWith("$(")) {
- serverUrl = "http://localhost:60610";
- logger.log(Level.INFO, "Using default Queue Server URL: " + serverUrl);
- }
-
- // Resolve API key with priority:
- // 1. Direct api_key preference (or QSERVER_HTTP_SERVER_API_KEY env var)
- // 2. Read from api_key_file path (or QSERVER_HTTP_SERVER_API_KEYFILE env var)
- String apiKey = resolveApiKey();
-
- RunEngineHttpClient.initialize(serverUrl, apiKey);
-
- Parent root = ViewFactory.APPLICATION.get();
-
- QueueServerInstance inst = new QueueServerInstance(this, root);
-
- DockItem tab = new DockItem(inst, root);
- // Register cleanup callback when tab is closed
- tab.addClosedNotification(AppLifecycle::shutdown);
- DockPane.getActiveDockPane().addTab(tab);
-
- return inst;
- }
-
- /**
- * Resolve the API key using the same priority as Python bluesky-widgets:
- * 1. Check QSERVER_HTTP_SERVER_API_KEY environment variable (via api_key preference)
- * 2. If not set, check QSERVER_HTTP_SERVER_API_KEYFILE environment variable (via api_key_file preference)
- * 3. If keyfile path is set, read the API key from that file
- *
- * @return The resolved API key, or null if not configured
- */
- private static String resolveApiKey() {
- // First priority: direct API key
- String apiKey = Preferences.api_key;
- // Check if the preference was expanded (not still $(VAR) syntax) and not empty
- if (apiKey != null && !apiKey.trim().isEmpty() && !apiKey.startsWith("$(")) {
- logger.log(Level.FINE, "Using API key from QSERVER_HTTP_SERVER_API_KEY");
- return apiKey.trim();
- }
-
- // Second priority: read from keyfile
- String keyFilePath = Preferences.api_key_file;
- if (keyFilePath != null && !keyFilePath.trim().isEmpty() && !keyFilePath.startsWith("$(")) {
- try {
- Path path = Paths.get(keyFilePath.trim());
- if (Files.exists(path)) {
- apiKey = Files.readString(path).trim();
- logger.log(Level.FINE, "Using API key from file: " + keyFilePath);
- return apiKey;
- } else {
- logger.log(Level.WARNING, "API key file not found: " + keyFilePath);
- }
- } catch (IOException e) {
- logger.log(Level.WARNING, "Failed to read API key from file: " + keyFilePath, e);
- }
- }
-
- logger.log(Level.WARNING, "No API key configured. Set QSERVER_HTTP_SERVER_API_KEY environment variable " +
- "or QSERVER_HTTP_SERVER_API_KEYFILE to point to a file containing the API key.");
- return null;
- }
-
- @Override public AppInstance create(java.net.URI resource) {
- return ApplicationService.createInstance(NAME);
- }
-}
\ No newline at end of file
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ApplicationController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ApplicationController.java
deleted file mode 100644
index 3c12a82e0b..0000000000
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ApplicationController.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.phoebus.applications.queueserver.controller;
-
-import org.phoebus.applications.queueserver.view.ViewFactory;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.scene.Node;
-import javafx.scene.Parent;
-import javafx.scene.control.Tab;
-import javafx.scene.control.TabPane;
-import javafx.scene.control.TableView;
-
-import java.net.URL;
-import java.util.ResourceBundle;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public final class ApplicationController implements Initializable {
-
- @FXML private Tab monitorQueueTab;
- @FXML private Tab editAndControlQueueTab;
- @FXML private TabPane tabPane;
-
- private static final Logger logger = Logger.getLogger(ApplicationController.class.getPackageName());
-
- @Override public void initialize(URL url, ResourceBundle rb) {
- logger.log(Level.FINE, "Initializing ApplicationController");
- monitorQueueTab.setContent(ViewFactory.MONITOR_QUEUE.get());
- editAndControlQueueTab.setContent(ViewFactory.EDIT_AND_CONTROL_QUEUE.get());
-
- // Disable focus traversal on all components
- disableFocusTraversal(monitorQueueTab.getContent());
- disableFocusTraversal(editAndControlQueueTab.getContent());
-
- logger.log(Level.FINE, "ApplicationController initialization complete");
- }
-
- /**
- * Recursively disables focus traversal on all nodes in the scene graph,
- * except for TableView which remains focus traversable for arrow key navigation.
- */
- private void disableFocusTraversal(Node node) {
- if (node == null) return;
-
- // Allow TableView to remain focus traversable for arrow key navigation
- if (!(node instanceof TableView)) {
- node.setFocusTraversable(false);
- }
-
- if (node instanceof Parent) {
- Parent parent = (Parent) node;
- for (Node child : parent.getChildrenUnmodifiable()) {
- disableFocusTraversal(child);
- }
- }
- }
-
-}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReManagerConnectionController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReManagerConnectionController.java
deleted file mode 100644
index 1b6aeb014a..0000000000
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReManagerConnectionController.java
+++ /dev/null
@@ -1,419 +0,0 @@
-package org.phoebus.applications.queueserver.controller;
-
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.phoebus.applications.queueserver.Preferences;
-import org.phoebus.applications.queueserver.api.StatusResponse;
-import org.phoebus.applications.queueserver.api.StatusWsMessage;
-import org.phoebus.applications.queueserver.client.QueueServerWebSocket;
-import org.phoebus.applications.queueserver.client.RunEngineService;
-import org.phoebus.applications.queueserver.util.AppLifecycle;
-import org.phoebus.applications.queueserver.util.PlansCache;
-import org.phoebus.applications.queueserver.util.PollCenter;
-import org.phoebus.applications.queueserver.util.StatusBus;
-import javafx.application.Platform;
-import javafx.fxml.FXML;
-import javafx.scene.control.Label;
-import javafx.scene.control.ToggleButton;
-
-import java.util.Map;
-import java.util.concurrent.ScheduledFuture;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public final class ReManagerConnectionController {
-
- @FXML private ToggleButton autoConnectToggle;
- @FXML private Label connectionStatusLabel;
-
- private final RunEngineService svc = new RunEngineService();
- private final ObjectMapper mapper = new ObjectMapper()
- .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- private ScheduledFuture> pollTask;
- private ScheduledFuture> reconnectTask;
- private ScheduledFuture> healthCheckTask;
- private QueueServerWebSocket statusWs;
- private volatile StatusResponse latestStatus = null;
- private volatile boolean websocketConnected = false;
- private volatile boolean attemptingConnection = false;
- private volatile long lastStatusUpdateTime = 0;
- private volatile long connectionAttemptStartTime = 0;
- private volatile int reconnectAttempts = 0;
- private volatile ConnectionState currentState = ConnectionState.DISCONNECTED;
- private static final Logger logger = Logger.getLogger(ReManagerConnectionController.class.getPackageName());
-
- // Status message timeout - if no status received for this long, show RED error
- private static final long STATUS_TIMEOUT_MS = 3000;
-
- // Connection attempt timeout - if no connection after this long, show NETWORK ERROR
- private static final long CONNECTION_ATTEMPT_TIMEOUT_MS = 5000;
-
- // Reconnect interval - how often to retry connection when it fails
- private static final long RECONNECT_INTERVAL_MS = 5000;
-
- // Connection states
- private enum ConnectionState {
- DISCONNECTED, // Grey - User manually disabled auto-connect
- CONNECTING, // Grey - Attempting to establish connection
- NETWORK_ERROR, // Dark blue - WebSocket can't connect (auth/network issue)
- NO_STATUS, // Red - WebSocket connected but no status messages from RE Manager
- CONNECTED // Green - WebSocket connected AND receiving status messages
- }
-
- @FXML
- public void initialize() {
- // Register shutdown callback for app lifecycle management
- AppLifecycle.registerShutdown(this::stop);
-
- // Start in auto-connect mode (toggle is selected by default in FXML)
- if (autoConnectToggle.isSelected()) {
- start();
- }
- }
-
- @FXML
- private void toggleConnection() {
- if (autoConnectToggle.isSelected()) {
- // User enabled auto-connect
- start();
- } else {
- // User disabled auto-connect
- stop();
- }
- }
-
- private void start() {
- if (pollTask != null && !pollTask.isDone()) return; // already running
- if (statusWs != null && statusWs.isConnected()) return; // already connected
-
- websocketConnected = false;
- attemptingConnection = false;
- latestStatus = null;
- lastStatusUpdateTime = 0;
- connectionAttemptStartTime = 0;
- reconnectAttempts = 0;
-
- if (Preferences.use_websockets) {
- logger.log(Level.INFO, "Starting status WebSocket connection");
- attemptWebSocketConnection();
- } else {
- logger.log(Level.INFO, "Starting status polling every " + Preferences.update_interval_ms + " milliseconds");
- attemptingConnection = true;
- connectionAttemptStartTime = System.currentTimeMillis();
- pollTask = PollCenter.everyMs(Preferences.update_interval_ms,
- this::queryStatusOnce, // background
- this::updateWidgets); // FX thread
- }
-
- // Start health check to monitor connection state
- if (healthCheckTask == null) {
- healthCheckTask = PollCenter.everyMs(500, this::checkConnectionHealth);
- }
- }
-
- private void attemptWebSocketConnection() {
- try {
- attemptingConnection = true;
- connectionAttemptStartTime = System.currentTimeMillis();
-
- statusWs = svc.createStatusWebSocket();
-
- // Buffer incoming WebSocket messages without immediately updating UI
- statusWs.addListener(msg -> {
- Map statusMap = msg.status();
- if (statusMap != null) {
- try {
- // Convert Map to StatusResponse and buffer it
- latestStatus = mapper.convertValue(statusMap, StatusResponse.class);
- // Record timestamp when we receive fresh data from WebSocket
- lastStatusUpdateTime = System.currentTimeMillis();
-
- // Mark WebSocket as connected on first successful message
- if (!websocketConnected) {
- websocketConnected = true;
- attemptingConnection = false;
- reconnectAttempts = 0;
- logger.log(Level.INFO, "WebSocket connection established");
- }
- } catch (Exception e) {
- logger.log(Level.WARNING, "Failed to parse status from WebSocket", e);
- }
- }
- });
-
- statusWs.connect();
-
- // Schedule throttled UI updates at the configured interval
- pollTask = PollCenter.everyMs(Preferences.update_interval_ms, () -> {
- // Check if WebSocket is still connected
- if (statusWs != null && !statusWs.isConnected()) {
- // WebSocket closed
- if (websocketConnected) {
- logger.log(Level.WARNING, "WebSocket connection lost");
- websocketConnected = false;
- attemptingConnection = false;
-
- // Auto-reconnect if toggle is still enabled
- if (autoConnectToggle.isSelected()) {
- scheduleReconnect();
- }
- }
- }
-
- StatusResponse status = latestStatus;
- if (status != null) {
- Platform.runLater(() -> updateWidgets(status));
- }
- });
- } catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to create WebSocket connection", e);
- websocketConnected = false;
- attemptingConnection = false;
-
- // Auto-reconnect if toggle is still enabled
- if (autoConnectToggle.isSelected()) {
- scheduleReconnect();
- }
- }
- }
-
- private void scheduleReconnect() {
- // Don't reconnect if user disabled auto-connect
- if (!autoConnectToggle.isSelected()) {
- return;
- }
-
- // Don't schedule if already scheduled and not done
- if (reconnectTask != null && !reconnectTask.isDone()) {
- logger.log(Level.FINE, "Reconnect already scheduled, skipping");
- return;
- }
-
- // Don't schedule if already attempting connection
- if (attemptingConnection) {
- logger.log(Level.FINE, "Connection attempt in progress, skipping reconnect schedule");
- return;
- }
-
- // Cancel any existing reconnect task
- if (reconnectTask != null) {
- reconnectTask.cancel(false);
- }
-
- reconnectAttempts++;
-
- logger.log(Level.INFO, "Scheduling reconnect attempt #" + reconnectAttempts + " in " + RECONNECT_INTERVAL_MS + "ms");
-
- reconnectTask = PollCenter.afterMs((int) RECONNECT_INTERVAL_MS, () -> {
- if (autoConnectToggle.isSelected() && !attemptingConnection) {
- logger.log(Level.INFO, "Attempting to reconnect (attempt #" + reconnectAttempts + ")");
-
- // Clean up existing connection
- if (statusWs != null) {
- statusWs.disconnect();
- statusWs = null;
- }
- if (pollTask != null) {
- pollTask.cancel(false);
- pollTask = null;
- }
-
- // Reset state before attempting reconnection
- websocketConnected = false;
-
- // Try to reconnect
- attemptWebSocketConnection();
- }
- });
- }
-
- private StatusResponse queryStatusOnce() {
- try {
- StatusResponse status = svc.status();
- if (status != null) {
- lastStatusUpdateTime = System.currentTimeMillis();
- if (attemptingConnection) {
- attemptingConnection = false;
- reconnectAttempts = 0;
- logger.log(Level.INFO, "HTTP connection established");
- }
- }
- return status;
- } catch (Exception ex) {
- logger.log(Level.FINE, "Status query failed: " + ex.getMessage());
- return null;
- }
- }
-
- private void checkConnectionHealth() {
- ConnectionState newState;
-
- if (!autoConnectToggle.isSelected()) {
- // User disabled auto-connect
- newState = ConnectionState.DISCONNECTED;
- } else if (Preferences.use_websockets) {
- // WebSocket mode
- if (attemptingConnection) {
- // Currently attempting to connect
- long timeSinceAttemptStart = System.currentTimeMillis() - connectionAttemptStartTime;
- if (timeSinceAttemptStart > CONNECTION_ATTEMPT_TIMEOUT_MS) {
- // Connection attempt timed out - trigger reconnect
- attemptingConnection = false;
- newState = ConnectionState.NETWORK_ERROR;
-
- // Clean up failed connection attempt
- if (pollTask != null) {
- pollTask.cancel(false);
- pollTask = null;
- }
- if (statusWs != null) {
- statusWs.disconnect();
- statusWs = null;
- }
-
- scheduleReconnect();
- } else {
- // Still trying to connect
- newState = ConnectionState.CONNECTING;
- }
- } else if (!websocketConnected) {
- // Not attempting and not connected
- newState = ConnectionState.NETWORK_ERROR;
-
- // Only schedule reconnect if no reconnect task is pending
- if ((reconnectTask == null || reconnectTask.isDone()) &&
- (statusWs == null || !statusWs.isConnected())) {
- scheduleReconnect();
- }
- } else if (lastStatusUpdateTime == 0) {
- // WebSocket connected but no status received yet - still establishing
- newState = ConnectionState.CONNECTING;
- } else {
- long timeSinceLastUpdate = System.currentTimeMillis() - lastStatusUpdateTime;
- if (timeSinceLastUpdate > STATUS_TIMEOUT_MS) {
- // WebSocket connected but no recent status - RE Manager issue
- newState = ConnectionState.NO_STATUS;
- } else {
- // All good - connected and receiving status
- newState = ConnectionState.CONNECTED;
- }
- }
- } else {
- // HTTP polling mode
- if (attemptingConnection) {
- // Currently attempting to connect
- long timeSinceAttemptStart = System.currentTimeMillis() - connectionAttemptStartTime;
- if (timeSinceAttemptStart > CONNECTION_ATTEMPT_TIMEOUT_MS) {
- // Connection attempt timed out
- attemptingConnection = false;
- newState = ConnectionState.NETWORK_ERROR;
- } else {
- // Still trying to connect
- newState = ConnectionState.CONNECTING;
- }
- } else if (lastStatusUpdateTime == 0) {
- // No successful status query yet
- newState = ConnectionState.NETWORK_ERROR;
- } else {
- long timeSinceLastUpdate = System.currentTimeMillis() - lastStatusUpdateTime;
- if (timeSinceLastUpdate > STATUS_TIMEOUT_MS) {
- // No recent status
- newState = ConnectionState.NETWORK_ERROR;
- } else {
- // Receiving status
- newState = ConnectionState.CONNECTED;
- }
- }
- }
-
- // Only update UI if state actually changed
- if (newState != currentState) {
- ConnectionState oldState = currentState;
- currentState = newState;
- logger.log(Level.FINE, "Connection state change: " + oldState + " → " + newState);
- Platform.runLater(() -> updateConnectionState(newState));
- }
- }
-
- private void stop() {
- logger.log(Level.INFO, "Stopping status monitoring");
-
- if (reconnectTask != null) {
- reconnectTask.cancel(true);
- reconnectTask = null;
- }
- if (healthCheckTask != null) {
- healthCheckTask.cancel(true);
- healthCheckTask = null;
- }
- if (pollTask != null) {
- pollTask.cancel(true);
- pollTask = null;
- }
- if (statusWs != null) {
- statusWs.disconnect();
- statusWs = null;
- }
-
- websocketConnected = false;
- reconnectAttempts = 0;
-
- // Don't clear latestStatus or push null to StatusBus
- // This preserves the last known state in all UI widgets
-
- currentState = ConnectionState.DISCONNECTED;
- updateConnectionState(ConnectionState.DISCONNECTED);
- }
-
- private void updateConnectionState(ConnectionState state) {
- switch (state) {
- case DISCONNECTED:
- connectionStatusLabel.setText("OFFLINE");
- connectionStatusLabel.setStyle("-fx-text-fill: grey;");
- autoConnectToggle.setText("Connect");
- latestStatus = null; // Clear cached status
- StatusBus.push(null); // Disable all controls
- break;
-
- case CONNECTING:
- connectionStatusLabel.setText("CONNECTING");
- connectionStatusLabel.setStyle("-fx-text-fill: grey;");
- autoConnectToggle.setText("Disconnect");
- // Don't push null here - preserve last known status during reconnection
- // This prevents unnecessary UI flickering and console monitor restarts
- break;
-
- case NETWORK_ERROR:
- connectionStatusLabel.setText("NETWORK");
- connectionStatusLabel.setStyle("-fx-text-fill: #00008B;"); // Dark blue
- autoConnectToggle.setText("Disconnect");
- latestStatus = null; // Clear cached status to prevent stale data from being pushed
- StatusBus.push(null); // Disable all controls
- break;
-
- case NO_STATUS:
- connectionStatusLabel.setText("STATUS");
- connectionStatusLabel.setStyle("-fx-text-fill: red;");
- autoConnectToggle.setText("Disconnect");
- latestStatus = null; // Clear cached status
- StatusBus.push(null); // Disable all controls
- break;
-
- case CONNECTED:
- connectionStatusLabel.setText("CONNECTED");
- connectionStatusLabel.setStyle("-fx-text-fill: green;");
- autoConnectToggle.setText("Disconnect");
- // Load allowed plans into shared cache (only fetches once)
- PlansCache.loadIfNeeded();
- // Don't push to StatusBus here - let updateWidgets do it
- break;
- }
- }
-
- private void updateWidgets(StatusResponse s) {
- if (s != null) {
- logger.log(Level.FINEST, "Status update: manager_state=" + s.managerState());
- StatusBus.push(s);
- }
- }
-}
diff --git a/app/queue-server/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor b/app/queue-server/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor
deleted file mode 100644
index 62a026f374..0000000000
--- a/app/queue-server/src/main/resources/META-INF/services/org.phoebus.framework.spi.AppDescriptor
+++ /dev/null
@@ -1 +0,0 @@
-org.phoebus.applications.queueserver.QueueServerApp
\ No newline at end of file
diff --git a/app/queue-server/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry b/app/queue-server/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry
deleted file mode 100644
index c83eb257e1..0000000000
--- a/app/queue-server/src/main/resources/META-INF/services/org.phoebus.ui.spi.MenuEntry
+++ /dev/null
@@ -1 +0,0 @@
-org.phoebus.applications.queueserver.QueueServerMenuEntry
\ No newline at end of file
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/messages.properties b/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/messages.properties
deleted file mode 100644
index fcb00c0408..0000000000
--- a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/messages.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-QueueServer=Queue Server
-QueueServerMenuPath=Utility
\ No newline at end of file
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/messages_fr.properties b/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/messages_fr.properties
deleted file mode 100644
index 2c49330622..0000000000
--- a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/messages_fr.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-QueueServer=Serveur de file d’attente
-QueueServerMenuPath=Outil
diff --git a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/Application.fxml b/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/Application.fxml
deleted file mode 100644
index 609601d802..0000000000
--- a/app/queue-server/src/main/resources/org/phoebus/applications/queueserver/view/Application.fxml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/websocket/client/pom.xml b/core/websocket/client/pom.xml
index 2b8ab7df2c..9e5b8093f5 100644
--- a/core/websocket/client/pom.xml
+++ b/core/websocket/client/pom.xml
@@ -29,6 +29,17 @@
${spring.boot.version}
+
+
org.junit.jupiter
junit-jupiter
diff --git a/phoebus-product/pom.xml b/phoebus-product/pom.xml
index d5426515b0..2eeea467e9 100644
--- a/phoebus-product/pom.xml
+++ b/phoebus-product/pom.xml
@@ -47,11 +47,6 @@
app-filebrowser
5.0.3-SNAPSHOT
-
- org.phoebus
- app-queue-server
- 5.0.3-SNAPSHOT
-
org.phoebus
app-probe