diff --git a/app/queue-server/pom.xml b/app/queue-server/pom.xml
index 5e97e07249..1310dac5f3 100644
--- a/app/queue-server/pom.xml
+++ b/app/queue-server/pom.xml
@@ -34,6 +34,12 @@
jackson-dataformat-yaml
2.19.1
+
+
+ org.python
+ jython-standalone
+ ${jython.version}
+
org.apache.poi
poi
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
index 5e1ca2034c..12aded73a9 100644
--- 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
@@ -16,7 +16,7 @@
@SuppressWarnings("nls")
public final class QueueServerApp implements AppResourceDescriptor {
- public static final Logger LOGGER = Logger.getLogger(QueueServerApp.class.getPackageName());
+ 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";
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineHttpClient.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineHttpClient.java
index b58da4ed1a..185f4e218b 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineHttpClient.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineHttpClient.java
@@ -48,7 +48,7 @@ public static RunEngineHttpClient get() {
private final String base;
private final String apiKey;
private final RateLimiter limiter;
- private static final Logger LOG = HttpSupport.LOG;
+ private static final Logger logger = HttpSupport.logger;
private RunEngineHttpClient(String baseUrl, String apiKey, double permitsPerSecond) {
this.http = HttpClient.newBuilder()
@@ -118,14 +118,14 @@ private T executeWithRetry(HttpRequest req, ApiEndpoint ep,
limiter.acquire();
long t0 = System.nanoTime();
try {
- LOG.log(Level.FINEST, ep + " attempt " + attempt);
+ logger.log(Level.FINEST, ep + " attempt " + attempt);
HttpResponse rsp = http.send(req, HttpResponse.BodyHandlers.ofString());
- LOG.log(Level.FINEST, ep + " " + rsp.statusCode() + " " + HttpSupport.elapsed(t0) + " ms");
+ logger.log(Level.FINEST, ep + " " + rsp.statusCode() + " " + HttpSupport.elapsed(t0) + " ms");
check(rsp, ep);
return reader.apply(rsp);
} catch (java.io.IOException ex) {
if (!HttpSupport.isRetryable(req) || attempt >= HttpSupport.MAX_RETRIES) throw ex;
- LOG.log(Level.WARNING, ep + " transport error (" + ex.getClass().getSimpleName() +
+ logger.log(Level.WARNING, ep + " transport error (" + ex.getClass().getSimpleName() +
"), retry in " + back + " ms (attempt " + attempt + ")");
Thread.sleep(back);
back = Math.round(back * HttpSupport.BACKOFF_MULTIPLIER);
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineService.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineService.java
index 5e42902b49..37b0572b83 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineService.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/client/RunEngineService.java
@@ -29,13 +29,13 @@
public final class RunEngineService {
private final RunEngineHttpClient http = RunEngineHttpClient.get();
- private static final Logger LOG = Logger.getLogger(RunEngineService.class.getName());
+ private static final Logger logger = Logger.getLogger(RunEngineService.class.getPackageName());
/* ---- Ping & status --------------------------------------------------- */
public Envelope> ping() throws Exception { return http.call(ApiEndpoint.PING, NoBody.INSTANCE); }
public StatusResponse status() throws Exception {
- LOG.log(Level.FINEST, "Fetching status");
+ logger.log(Level.FINEST, "Fetching status");
return http.send(ApiEndpoint.STATUS, NoBody.INSTANCE, StatusResponse.class);
}
public Envelope> configGet() throws Exception { return http.call(ApiEndpoint.CONFIG_GET, NoBody.INSTANCE); }
@@ -168,7 +168,7 @@ public Envelope> queueItemUpdate(QueueItem item) throws Exception {
/* ───────── Console monitor ───────── */
public InputStream streamConsoleOutput() throws Exception {
- LOG.info("Opening console output stream");
+ logger.log(Level.FINE, "Opening console output stream");
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(http.getBaseUrl() + ApiEndpoint.STREAM_CONSOLE_OUTPUT.endpoint().path()))
.header("Authorization", "ApiKey " + http.getApiKey())
@@ -178,10 +178,10 @@ public InputStream streamConsoleOutput() throws Exception {
HttpResponse rsp =
http.httpClient().send(req, HttpResponse.BodyHandlers.ofInputStream());
if (rsp.statusCode() < 200 || rsp.statusCode() >= 300) {
- LOG.log(Level.WARNING, "Console stream failed with HTTP " + rsp.statusCode());
+ logger.log(Level.WARNING, "Console stream failed with HTTP " + rsp.statusCode());
throw new IOException("console stream - HTTP " + rsp.statusCode());
}
- LOG.info("Console output stream opened successfully");
+ logger.log(Level.FINE, "Console output stream opened successfully");
return rsp.body();
}
@@ -251,7 +251,7 @@ public Envelope> rePause(String option) throws Exception {
public Envelope> plansAllowed() throws Exception { return http.call(ApiEndpoint.PLANS_ALLOWED, NoBody.INSTANCE); }
public Map plansAllowedRaw() throws Exception {
- LOG.log(Level.FINE, "Fetching plans allowed (raw)");
+ logger.log(Level.FINE, "Fetching plans allowed (raw)");
return http.send(ApiEndpoint.PLANS_ALLOWED, NoBody.INSTANCE);
}
public Envelope> devicesAllowed() throws Exception { return http.call(ApiEndpoint.DEVICES_ALLOWED, NoBody.INSTANCE); }
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
index 91e337fbef..3c12a82e0b 100644
--- 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
@@ -3,24 +3,55 @@
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;
-
- private static final Logger LOG = Logger.getLogger(ApplicationController.class.getName());
+ @FXML private TabPane tabPane;
+
+ private static final Logger logger = Logger.getLogger(ApplicationController.class.getPackageName());
@Override public void initialize(URL url, ResourceBundle rb) {
- LOG.info("Initializing ApplicationController");
+ logger.log(Level.FINE, "Initializing ApplicationController");
monitorQueueTab.setContent(ViewFactory.MONITOR_QUEUE.get());
editAndControlQueueTab.setContent(ViewFactory.EDIT_AND_CONTROL_QUEUE.get());
- LOG.info("ApplicationController initialization complete");
+
+ // 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/EditAndControlQueueController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/EditAndControlQueueController.java
index e0d5b09707..8fb4e4342a 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/EditAndControlQueueController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/EditAndControlQueueController.java
@@ -18,11 +18,11 @@ public class EditAndControlQueueController implements Initializable {
@FXML private AnchorPane planQueueContainer;
@FXML private AnchorPane planHistoryContainer;
- private static final Logger LOG = Logger.getLogger(EditAndControlQueueController.class.getName());
+ private static final Logger logger = Logger.getLogger(EditAndControlQueueController.class.getPackageName());
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
- LOG.info("Initializing EditAndControlQueueController");
+ logger.log(Level.FINE, "Initializing EditAndControlQueueController");
loadInto(runningPlanContainer, "/org/phoebus/applications/queueserver/view/ReRunningPlan.fxml", new ReRunningPlanController(false));
loadInto(planQueueContainer, "/org/phoebus/applications/queueserver/view/RePlanQueue.fxml", new RePlanQueueController(false));
loadInto(planHistoryContainer, "/org/phoebus/applications/queueserver/view/RePlanHistory.fxml", new RePlanHistoryController(false));
@@ -40,7 +40,7 @@ private void loadInto(AnchorPane container, String fxml, Object controller) {
AnchorPane.setLeftAnchor(view, 0.0);
AnchorPane.setRightAnchor(view, 0.0);
} catch (IOException e) {
- LOG.log(Level.SEVERE, "Failed to load FXML: " + fxml, e);
+ logger.log(Level.SEVERE, "Failed to load FXML: " + fxml, e);
}
}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/MonitorQueueController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/MonitorQueueController.java
index de3d6ccef2..c6f04ca33e 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/MonitorQueueController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/MonitorQueueController.java
@@ -25,7 +25,7 @@ public class MonitorQueueController implements Initializable {
@FXML private VBox stack;
private final Map savedHeights = new HashMap<>();
- private static final Logger LOG = Logger.getLogger(MonitorQueueController.class.getName());
+ private static final Logger logger = Logger.getLogger(MonitorQueueController.class.getPackageName());
private static final String BAR_NORMAL =
"-fx-background-color: linear-gradient(to bottom, derive(-fx-base,15%) 0%, derive(-fx-base,-5%) 100%);" +
@@ -34,7 +34,7 @@ public class MonitorQueueController implements Initializable {
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
- LOG.info("Initializing MonitorQueueController");
+ logger.log(Level.FINE, "Initializing MonitorQueueController");
loadInto(runningPlanContainer, "/org/phoebus/applications/queueserver/view/ReRunningPlan.fxml", new ReRunningPlanController(true));
loadInto(planQueueContainer, "/org/phoebus/applications/queueserver/view/RePlanQueue.fxml", new RePlanQueueController(true));
loadInto(planHistoryContainer, "/org/phoebus/applications/queueserver/view/RePlanHistory.fxml", new RePlanHistoryController(true));
@@ -76,7 +76,7 @@ private void loadInto(AnchorPane container, String fxml, Object controller) {
AnchorPane.setLeftAnchor(view, 0.0);
AnchorPane.setRightAnchor(view, 0.0);
} catch (IOException e) {
- LOG.log(Level.SEVERE, "Failed to load FXML: " + fxml, e);
+ logger.log(Level.SEVERE, "Failed to load FXML: " + fxml, e);
}
}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReConsoleMonitorController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReConsoleMonitorController.java
index 157bd45617..b826304ab1 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReConsoleMonitorController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReConsoleMonitorController.java
@@ -200,13 +200,9 @@ private static final class ConsoleTextBuffer {
private int line = 0;
private int pos = 0;
- private boolean progressActive = false; // overwriting a tqdm bar
- private String lastVisualLine = ""; // for duplicate-collapse
-
private static final String NL = "\n";
private static final String CR = "\r";
- private static final char ESC = 0x1B;
- private static final String LOG_PREFIXES = "IWEDE"; // info/warn/error/debug/extra
+ private static final String UP_ONE_LINE = "\u001b[A"; // ESC[A
ConsoleTextBuffer(int hardLimit) {
this.hardLimit = Math.max(hardLimit, 0);
@@ -215,93 +211,82 @@ private static final class ConsoleTextBuffer {
void clear() {
buf.clear();
line = pos = 0;
- progressActive = false;
- lastVisualLine = "";
}
void addMessage(String msg) {
while (!msg.isEmpty()) {
+ // Find next control sequence
+ int nlIdx = msg.indexOf(NL);
+ int crIdx = msg.indexOf(CR);
+ int upIdx = msg.indexOf(UP_ONE_LINE);
- /* next control-char position */
- int next = minPos(msg.indexOf(NL), msg.indexOf(CR), msg.indexOf(ESC));
+ int next = minPos(nlIdx, crIdx, upIdx);
if (next < 0) next = msg.length();
- /* ---------------------------------------------------- *
- * FRAGMENT: plain text until control char *
- * ---------------------------------------------------- */
+ // ----------------------------------------------------
+ // FRAGMENT: plain text until control char
+ // ----------------------------------------------------
if (next != 0) {
String frag = msg.substring(0, next);
msg = msg.substring(next);
- /* are we switching from progress bar → normal log? */
- if (progressActive &&
- (frag.startsWith("[") || frag.startsWith("TEST") ||
- frag.startsWith("Returning") || frag.startsWith("history")))
- {
- newline(); // finish bar line
- progressActive = false;
+ // Ensure we have at least one line
+ if (buf.isEmpty()) {
+ buf.add("");
}
ensureLineExists(line);
- /* pad if cursor past EOL */
- if (pos > buf.get(line).length()) {
- buf.set(line, buf.get(line) + " ".repeat(pos - buf.get(line).length()));
+ // Extend current line with spaces if cursor is past EOL
+ int lineLen = buf.get(line).length();
+ if (lineLen < pos) {
+ buf.set(line, buf.get(line) + " ".repeat(pos - lineLen));
}
- /* overwrite / extend */
- StringBuilder sb = new StringBuilder(buf.get(line));
- if (pos + frag.length() > sb.length()) {
- sb.setLength(pos);
- sb.append(frag);
- } else {
- sb.replace(pos, pos + frag.length(), frag);
- }
- buf.set(line, sb.toString());
- pos += frag.length();
-
- /* flag if this fragment looks like a tqdm bar *
- * (contains “%|” and the typical frame pattern) */
- if (frag.contains("%|")) progressActive = true;
+ // Insert/overwrite fragment at current position
+ String currentLine = buf.get(line);
+ String before = currentLine.substring(0, Math.min(pos, currentLine.length()));
+ String after = currentLine.substring(Math.min(pos + frag.length(), currentLine.length()));
+ buf.set(line, before + frag + after);
+ pos += frag.length();
continue;
}
- /* ---------------------------------------------------- *
- * CONTROL CHAR *
- * ---------------------------------------------------- */
- char c = msg.charAt(0);
+ // ----------------------------------------------------
+ // CONTROL SEQUENCES
+ // ----------------------------------------------------
- if (c == '\n') { // LF: finish logical line
- newline();
- progressActive = false; // tqdm ends with a real LF
- msg = msg.substring(1);
+ if (nlIdx == 0) {
+ // Newline: move to next line
+ line++;
+ if (line >= buf.size()) {
+ buf.add("");
+ }
+ pos = 0;
+ msg = msg.substring(NL.length());
}
- else if (c == '\r') { // CR: return to start of SAME line
+ else if (crIdx == 0) {
+ // Carriage return: move to beginning of current line
pos = 0;
- msg = msg.substring(1);
+ msg = msg.substring(CR.length());
}
- else if (c == ESC) { // ESC [ n A (cursor-up)
- int idx = 1;
- if (idx < msg.length() && msg.charAt(idx) == '[') {
- idx++;
- int startDigits = idx;
- while (idx < msg.length() && Character.isDigit(msg.charAt(idx))) idx++;
- if (idx < msg.length() && msg.charAt(idx) == 'A') {
- int nUp = (idx == startDigits) ? 1
- : Math.max(1, Integer.parseInt(msg.substring(startDigits, idx)));
- line = Math.max(0, line - nUp);
- pos = 0;
- ensureLineExists(line);
- msg = msg.substring(idx + 1);
- continue;
- }
+ else if (upIdx == 0) {
+ // Move up one line
+ if (line > 0) {
+ line--;
+ }
+ pos = 0;
+ msg = msg.substring(UP_ONE_LINE.length());
+ }
+ else {
+ // Shouldn't happen, but handle gracefully
+ if (!msg.isEmpty()) {
+ ensureLineExists(line);
+ buf.set(line, buf.get(line) + msg.charAt(0));
+ pos++;
+ msg = msg.substring(1);
}
- /* unknown sequence → treat ESC literally */
- ensureLineExists(line);
- buf.set(line, buf.get(line) + c);
- pos++;
- msg = msg.substring(1);
}
}
trim();
@@ -309,8 +294,12 @@ else if (c == ESC) { // ESC [ n A (cursor-up)
String tail(int n) {
if (n <= 0) return "";
- boolean sentinel = !buf.isEmpty() && buf.get(buf.size() - 1).isEmpty();
- int visible = buf.size() - (sentinel ? 1 : 0);
+
+ // Remove trailing empty line if present
+ int visible = buf.size();
+ if (visible > 0 && buf.get(visible - 1).isEmpty()) {
+ visible--;
+ }
int start = Math.max(0, visible - n);
StringBuilder out = new StringBuilder();
@@ -322,33 +311,25 @@ String tail(int n) {
}
private void ensureLineExists(int idx) {
- while (buf.size() <= idx) buf.add("");
- }
-
- private void newline() {
- /* avoid storing duplicate visual lines (stream + poll) */
- String justFinished = buf.get(line);
- if (!justFinished.equals(lastVisualLine)) {
- lastVisualLine = justFinished;
- line++;
- pos = 0;
- ensureLineExists(line);
- } else {
- /* discard duplicate; stay on existing last line */
- pos = buf.get(line).length();
+ while (buf.size() <= idx) {
+ buf.add("");
}
}
private void trim() {
- boolean sentinel = !buf.isEmpty() && buf.get(buf.size() - 1).isEmpty();
- int quota = hardLimit + (sentinel ? 1 : 0);
- while (buf.size() > quota) buf.remove(0);
- if (line >= buf.size()) line = buf.size() - 1;
+ // Keep some buffer beyond hardLimit to avoid constant trimming
+ int maxAllowed = hardLimit + 100;
+ while (buf.size() > maxAllowed) {
+ buf.remove(0);
+ line = Math.max(0, line - 1);
+ }
}
private static int minPos(int... p) {
int m = Integer.MAX_VALUE;
- for (int v : p) if (v >= 0 && v < m) m = v;
+ for (int v : p) {
+ if (v >= 0 && v < m) m = v;
+ }
return m == Integer.MAX_VALUE ? -1 : m;
}
}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReEnvironmentControlsController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReEnvironmentControlsController.java
index be72dfceb5..3a3bd3e10a 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReEnvironmentControlsController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReEnvironmentControlsController.java
@@ -14,6 +14,7 @@
import java.net.URL;
import java.util.Map;
import java.util.ResourceBundle;
+import java.util.logging.Level;
import java.util.logging.Logger;
public final class ReEnvironmentControlsController implements Initializable {
@@ -23,7 +24,7 @@ public final class ReEnvironmentControlsController implements Initializable {
@FXML private Button destroyBtn;
private final RunEngineService svc = new RunEngineService();
- private final Logger LOG = Logger.getLogger(getClass().getName());
+ private static final Logger logger = Logger.getLogger(ReEnvironmentControlsController.class.getPackageName());
@Override public void initialize(URL url, ResourceBundle rb) {
StatusBus.latest().addListener(this::onStatus);
@@ -57,12 +58,12 @@ private void refreshButtons(Object statusObj) {
@FXML private void open() {
try { svc.environmentOpen(); }
- catch (Exception ex) { LOG.warning("environmentOpen: " + ex); }
+ catch (Exception ex) { logger.log(Level.WARNING, "environmentOpen: " + ex); }
}
@FXML private void close() {
try { svc.environmentClose(); }
- catch (Exception ex) { LOG.warning("environmentClose: " + ex); }
+ catch (Exception ex) { logger.log(Level.WARNING, "environmentClose: " + ex); }
}
@FXML private void destroy() {
@@ -75,9 +76,9 @@ private void refreshButtons(Object statusObj) {
if (response == ButtonType.OK) {
try {
svc.environmentDestroy();
- LOG.info("Environment destroyed successfully");
+ logger.log(Level.FINE, "Environment destroyed successfully");
} catch (Exception ex) {
- LOG.warning("environmentDestroy: " + ex);
+ logger.log(Level.WARNING, "environmentDestroy: " + ex);
}
}
});
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReExecutionControlsController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReExecutionControlsController.java
index b8ab0f162a..a78bd636cd 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReExecutionControlsController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReExecutionControlsController.java
@@ -12,6 +12,7 @@
import java.net.URL;
import java.util.Map;
import java.util.ResourceBundle;
+import java.util.logging.Level;
import java.util.logging.Logger;
public final class ReExecutionControlsController implements Initializable {
@@ -20,7 +21,7 @@ public final class ReExecutionControlsController implements Initializable {
stopBtn, abortBtn, haltBtn;
private final RunEngineService svc = new RunEngineService();
- private final Logger LOG = Logger.getLogger(getClass().getName());
+ private static final Logger logger = Logger.getLogger(ReExecutionControlsController.class.getPackageName());
@Override public void initialize(URL url, ResourceBundle rb) {
@@ -109,6 +110,6 @@ else if (statusObj instanceof Map,?> m) {
private void call(String label, Runnable r) {
try { r.run(); }
- catch (Exception ex) { LOG.warning(label + ": " + ex); }
+ catch (Exception ex) { logger.log(Level.WARNING, label + ": " + ex); }
}
}
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
index 8da791110d..4c66018b5e 100644
--- 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
@@ -21,14 +21,14 @@ public final class ReManagerConnectionController {
private final RunEngineService svc = new RunEngineService();
private ScheduledFuture> pollTask;
private static final int PERIOD_SEC = 1;
- private static final Logger LOG = Logger.getLogger(ReManagerConnectionController.class.getName());
+ private static final Logger logger = Logger.getLogger(ReManagerConnectionController.class.getPackageName());
@FXML private void connect() { startPolling(); }
@FXML private void disconnect() { stopPolling(); }
private void startPolling() {
if (pollTask != null && !pollTask.isDone()) return; // already running
- LOG.info("Starting connection polling every " + PERIOD_SEC + " seconds");
+ logger.log(Level.FINE, "Starting connection polling every " + PERIOD_SEC + " seconds");
showPending(); // UI while waiting
updateWidgets(queryStatusOnce());
@@ -42,13 +42,13 @@ private StatusResponse queryStatusOnce() {
try {
return svc.status();
} catch (Exception ex) {
- LOG.log(Level.FINE, "Status query failed: " + ex.getMessage());
+ logger.log(Level.FINE, "Status query failed: " + ex.getMessage());
return null;
}
}
private void stopPolling() {
- LOG.info("Stopping connection polling");
+ logger.log(Level.FINE, "Stopping connection polling");
if (pollTask != null) pollTask.cancel(true);
pollTask = null;
StatusBus.push(null);
@@ -74,7 +74,7 @@ private void showOnline() {
private void updateWidgets(StatusResponse s) {
StatusBus.push((s));
if (s != null) {
- LOG.log(Level.FINEST, "Status update: manager_state=" + s.managerState());
+ logger.log(Level.FINEST, "Status update: manager_state=" + s.managerState());
showOnline();
} else {
showPending(); // keep polling; user may Disconnect
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanEditorController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanEditorController.java
index 587a979aa1..74fa5450ab 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanEditorController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanEditorController.java
@@ -7,6 +7,7 @@
import org.phoebus.applications.queueserver.view.PlanEditEvent;
import org.phoebus.applications.queueserver.view.TabSwitchEvent;
import org.phoebus.applications.queueserver.view.ItemUpdateEvent;
+import org.phoebus.applications.queueserver.util.PythonParameterConverter;
import com.fasterxml.jackson.databind.ObjectMapper;
import javafx.application.Platform;
import javafx.geometry.Insets;
@@ -50,7 +51,7 @@ public class RePlanEditorController implements Initializable {
@FXML private TableColumn valueCol;
private final RunEngineService svc = new RunEngineService();
- private static final Logger LOG = Logger.getLogger(RePlanEditorController.class.getName());
+ private static final Logger logger = Logger.getLogger(RePlanEditorController.class.getPackageName());
private final ObservableList parameterRows = FXCollections.observableArrayList();
private final Map> allowedPlans = new HashMap<>();
private final Map> allowedInstructions = new HashMap<>();
@@ -63,6 +64,8 @@ public class RePlanEditorController implements Initializable {
private final ObjectMapper objectMapper = new ObjectMapper();
// Store original parameter values for reset functionality
private final Map originalParameterValues = new HashMap<>();
+ // Python-based parameter converter
+ private final PythonParameterConverter pythonConverter = new PythonParameterConverter();
private class EditableTableCell extends TableCell {
private TextField textField;
@@ -88,9 +91,11 @@ protected void updateItem(String item, boolean empty) {
setText(getString());
setGraphic(null);
- // Style based on enabled state and add tooltip
+ // Style based on enabled state and validation
if (!row.isEnabled()) {
setStyle("-fx-text-fill: grey;");
+ } else if (!row.validate(pythonConverter)) {
+ setStyle("-fx-text-fill: red;");
} else {
setStyle("");
}
@@ -132,15 +137,19 @@ public void commitEdit(String newValue) {
ParameterRow row = getTableRow().getItem();
if (row != null) {
row.setValue(newValue);
+
+ // Update cell color based on Python validation
+ updateValidationColor(row);
+
switchToEditingMode();
updateButtonStates();
- // Update cell color based on validation
- updateValidationColor(row);
}
}
private void updateValidationColor(ParameterRow row) {
- if (row.validate()) {
+ if (!row.isEnabled()) {
+ setStyle("-fx-text-fill: grey;");
+ } else if (row.validate(pythonConverter)) {
setStyle("");
} else {
setStyle("-fx-text-fill: red;");
@@ -208,69 +217,7 @@ public ParameterRow(String name, boolean enabled, String value, String descripti
public boolean isOptional() { return isOptional.get(); }
public Object getDefaultValue() { return defaultValue; }
- public Object getParsedValue() {
- if (!enabled.get()) return defaultValue;
-
- String valueStr = value.get();
- if (valueStr == null || valueStr.trim().isEmpty()) {
- return defaultValue;
- }
-
- try {
- return parseLiteralValue(valueStr);
- } catch (Exception e) {
- return valueStr;
- }
- }
-
- private Object parseLiteralValue(String valueStr) throws Exception {
- valueStr = valueStr.trim();
-
- // Handle None/null
- if ("None".equals(valueStr) || "null".equals(valueStr)) {
- return null;
- }
-
- // Handle booleans
- if ("True".equals(valueStr) || "true".equals(valueStr)) {
- return true;
- }
- if ("False".equals(valueStr) || "false".equals(valueStr)) {
- return false;
- }
-
- // Handle strings (quoted)
- if ((valueStr.startsWith("'") && valueStr.endsWith("'")) ||
- (valueStr.startsWith("\"") && valueStr.endsWith("\""))) {
- return valueStr.substring(1, valueStr.length() - 1);
- }
-
- // Handle numbers
- try {
- if (valueStr.contains(".")) {
- return Double.parseDouble(valueStr);
- } else {
- return Long.parseLong(valueStr);
- }
- } catch (NumberFormatException e) {
- // Continue to other parsing attempts
- }
-
- // Handle lists and dicts using JSON parsing (similar to Python's literal_eval)
- if (valueStr.startsWith("[") || valueStr.startsWith("{")) {
- try {
- ObjectMapper mapper = new ObjectMapper();
- return mapper.readValue(valueStr, Object.class);
- } catch (Exception e) {
- // Fall back to string if JSON parsing fails
- }
- }
-
- // Default to string
- return valueStr;
- }
-
- public boolean validate() {
+ public boolean validate(PythonParameterConverter converter) {
if (!enabled.get()) {
return true; // Disabled parameters are always valid
}
@@ -280,8 +227,18 @@ public boolean validate() {
return isOptional.get();
}
+ // Validate using Python converter
try {
- getParsedValue();
+ List testParams = List.of(
+ new PythonParameterConverter.ParameterInfo(
+ getName(),
+ valueStr,
+ true,
+ isOptional.get(),
+ getDefaultValue()
+ )
+ );
+ converter.convertParameters(testParams);
return true;
} catch (Exception e) {
return false;
@@ -432,10 +389,10 @@ private void loadAllowedPlansAndInstructions() {
allowedPlans.put(planName, planInfo);
}
} else {
- LOG.log(Level.WARNING, "No 'plans_allowed' key in response. Keys: " + responseMap.keySet());
+ logger.log(Level.WARNING, "No 'plans_allowed' key in response. Keys: " + responseMap.keySet());
}
} else {
- LOG.log(Level.WARNING, "Plans response failed. Response: " + responseMap);
+ logger.log(Level.WARNING, "Plans response failed. Response: " + responseMap);
}
allowedInstructions.clear();
@@ -450,7 +407,7 @@ private void loadAllowedPlansAndInstructions() {
});
} catch (Exception e) {
- LOG.log(Level.WARNING, "Failed to load plans", e);
+ logger.log(Level.WARNING, "Failed to load plans", e);
}
}).start();
}
@@ -545,9 +502,6 @@ private void loadParametersForSelection(String selectedName) {
isEnabled = true;
} else if (defaultValue != null) {
currentValue = String.valueOf(defaultValue);
- if (!isEditMode) {
- currentValue += " (default)";
- }
}
ParameterRow row = new ParameterRow(paramName, isEnabled, currentValue, description, isOptional, defaultValue);
@@ -642,7 +596,7 @@ private void autoResizeColumns() {
private boolean areParametersValid() {
boolean allValid = true;
for (ParameterRow row : parameterRows) {
- boolean rowValid = row.validate();
+ boolean rowValid = row.validate(pythonConverter);
if (!rowValid) {
allValid = false;
}
@@ -681,8 +635,32 @@ private void updateButtonStates() {
cancelBtn.setDisable(!isEditMode);
}
+ /**
+ * Build kwargs map using Python-based type conversion.
+ * All type conversion is handled by Python script using ast.literal_eval.
+ */
+ private Map buildKwargsWithPython() {
+ List paramInfos = new ArrayList<>();
+
+ for (ParameterRow row : parameterRows) {
+ PythonParameterConverter.ParameterInfo paramInfo =
+ new PythonParameterConverter.ParameterInfo(
+ row.getName(),
+ row.getValue(),
+ row.isEnabled(),
+ row.isOptional(),
+ row.getDefaultValue()
+ );
+ paramInfos.add(paramInfo);
+ }
+
+ // Use Python to convert parameters - no Java fallback
+ return pythonConverter.convertParameters(paramInfos);
+ }
+
private void addItemToQueue() {
if (!areParametersValid()) {
+ showValidationError("Some parameters have invalid values. Please check the red fields.");
return;
}
@@ -693,13 +671,9 @@ private void addItemToQueue() {
}
String itemType = planRadBtn.isSelected() ? "plan" : "instruction";
- Map kwargs = new HashMap<>();
- for (ParameterRow row : parameterRows) {
- if (row.isEnabled()) {
- kwargs.put(row.getName(), row.getParsedValue());
- }
- }
+ // Use Python-based parameter conversion
+ Map kwargs = buildKwargsWithPython();
QueueItem item = new QueueItem(
itemType,
@@ -732,24 +706,45 @@ private void addItemToQueue() {
TabSwitchEvent.getInstance().switchToTab("Plan Viewer");
exitEditMode();
showItemPreview();
+ } else {
+ showValidationError("Failed to add item to queue: " + response.msg());
}
});
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.WARNING, "Failed to add item to queue", e);
+ Platform.runLater(() -> {
+ String errorMsg = e.getMessage();
+ if (errorMsg == null || errorMsg.isEmpty()) {
+ errorMsg = e.getClass().getSimpleName();
+ }
+ showValidationError("Failed to add item to queue: " + errorMsg);
+ });
}
}).start();
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Failed to add item to queue", e);
+ Platform.runLater(() -> {
+ showValidationError("Failed to add item: " + e.getMessage());
+ });
}
}
+ private void showValidationError(String message) {
+ Alert alert = new Alert(Alert.AlertType.ERROR);
+ alert.setTitle("Validation Error");
+ alert.setHeaderText(null);
+ alert.setContentText(message);
+ alert.showAndWait();
+ }
+
private void saveItem() {
if (!isEditMode || currentItem == null) {
return;
}
if (!areParametersValid()) {
+ showValidationError("Some parameters have invalid values. Please check the red fields.");
return;
}
@@ -760,13 +755,9 @@ private void saveItem() {
}
String itemType = planRadBtn.isSelected() ? "plan" : "instruction";
- Map kwargs = new HashMap<>();
- for (ParameterRow row : parameterRows) {
- if (row.isEnabled()) {
- kwargs.put(row.getName(), row.getParsedValue());
- }
- }
+ // Use Python-based parameter conversion
+ Map kwargs = buildKwargsWithPython();
QueueItem updatedItem = new QueueItem(
itemType,
@@ -795,16 +786,26 @@ private void saveItem() {
exitEditMode();
showItemPreview();
} else {
- LOG.log(Level.WARNING, "Save failed: " + response.msg());
+ showValidationError("Failed to save item: " + response.msg());
}
});
} catch (Exception e) {
- LOG.log(Level.WARNING, "Save error", e);
+ logger.log(Level.WARNING, "Failed to save item", e);
+ Platform.runLater(() -> {
+ String errorMsg = e.getMessage();
+ if (errorMsg == null || errorMsg.isEmpty()) {
+ errorMsg = e.getClass().getSimpleName();
+ }
+ showValidationError("Failed to save item: " + errorMsg);
+ });
}
}).start();
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Failed to save item", e);
+ Platform.runLater(() -> {
+ showValidationError("Failed to save item: " + e.getMessage());
+ });
}
}
@@ -965,7 +966,7 @@ private void processBatchFile(String filePath, String fileType) {
}
} catch (Exception e) {
- LOG.log(Level.WARNING, "Batch file processing error", e);
+ logger.log(Level.WARNING, "Batch file processing error", e);
Platform.runLater(() -> {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Error");
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanHistoryController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanHistoryController.java
index c2a1e161c9..395c55e1a0 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanHistoryController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanHistoryController.java
@@ -29,6 +29,7 @@
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
+import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -49,8 +50,8 @@ public final class RePlanHistoryController implements Initializable {
private List stickySel = List.of();
private boolean ignoreSel = false;
- private static final Logger LOG =
- Logger.getLogger(RePlanHistoryController.class.getName());
+ private static final Logger logger =
+ Logger.getLogger(RePlanHistoryController.class.getPackageName());
private final boolean viewOnly;
@@ -123,7 +124,7 @@ private void refresh(StatusResponse st) {
ignoreSel = false;
restoreSelection(stickySel);
} catch (Exception ex) {
- LOG.warning("History refresh failed: "+ex.getMessage());
+ logger.log(Level.WARNING, "History refresh failed", ex);
}
}
@@ -188,13 +189,13 @@ private void copySelectedToQueue() {
new QueueItemAddBatch(clones, "GUI Client", "primary");
svc.queueItemAddBatch(req); // service takes DTO, not Map
} catch (Exception ex) {
- LOG.warning("Copy-to-Queue failed: "+ex.getMessage());
+ logger.log(Level.WARNING, "Copy-to-Queue failed", ex);
}
}
private void clearHistory() {
try { svc.historyClear(); }
- catch (Exception ex) { LOG.warning("Clear-history failed: "+ex.getMessage()); }
+ catch (Exception ex) { logger.log(Level.WARNING, "Clear-history failed", ex); }
}
private void exportHistory(PlanHistorySaver.Format fmt, String ext) {
@@ -209,9 +210,9 @@ private void exportHistory(PlanHistorySaver.Format fmt, String ext) {
HistoryGetPayload hp = svc.historyGetTyped();
List items = hp.items();
PlanHistorySaver.save(items, f, fmt);
- LOG.info(() -> "Exported plan history → " + f);
+ logger.log(Level.FINE, () -> "Exported plan history → " + f);
} catch (Exception e) {
- LOG.warning("Export history failed: " + e.getMessage());
+ logger.log(Level.WARNING, "Export history failed", e);
}
});
}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanQueueController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanQueueController.java
index b1c2183264..d1c98a205e 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanQueueController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanQueueController.java
@@ -22,6 +22,7 @@
import java.net.URL;
import java.util.*;
+import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -42,8 +43,8 @@ public final class RePlanQueueController implements Initializable {
private List stickySel = List.of(); // last user selection
private boolean ignoreSticky= false; // guard while we rebuild
- private static final Logger LOG =
- Logger.getLogger(RePlanQueueController.class.getName());
+ private static final Logger logger =
+ Logger.getLogger(RePlanQueueController.class.getPackageName());
private final boolean viewOnly;
@@ -136,7 +137,7 @@ private void refresh(StatusResponse st, Collection explicitFocus) {
.map(StatusResponse.PlanQueueMode::loop)
.orElse(false));
} catch (Exception ex) {
- LOG.warning("Queue refresh failed: " + ex.getMessage());
+ logger.log(Level.WARNING, "Queue refresh failed: " + ex.getMessage());
}
}
@@ -224,7 +225,7 @@ private void moveRelative(int delta) {
List focus = selectedUids();
try { sendMove(focus, rows.get(ref).uid(), delta<0); refreshLater(focus); }
- catch (Exception ex) { LOG.warning("Move failed: "+ex.getMessage()); }
+ catch (Exception ex) { logger.log(Level.WARNING, "Move failed: "+ex.getMessage()); }
}
private void moveAbsolute(int targetRow) {
if (rows.isEmpty()) return;
@@ -236,7 +237,7 @@ private void moveAbsolute(int targetRow) {
boolean before = targetRow < selRows.get(0);
List focus = selectedUids();
try { sendMove(focus, rows.get(targetRow).uid(), before); refreshLater(focus); }
- catch (Exception ex) { LOG.warning("Move-abs failed: "+ex.getMessage()); }
+ catch (Exception ex) { logger.log(Level.WARNING, "Move-abs failed: "+ex.getMessage()); }
}
private void deleteSelected() {
@@ -249,7 +250,7 @@ private void deleteSelected() {
try {
svc.queueItemRemoveBatch(Map.of("uids", selectedUids()));
refreshLater(nextFocus==null? List.of() : List.of(nextFocus));
- } catch (Exception ex) { LOG.warning("Delete failed: "+ex.getMessage()); }
+ } catch (Exception ex) { logger.log(Level.WARNING, "Delete failed: "+ex.getMessage()); }
}
private void duplicateSelected() {
@@ -265,7 +266,7 @@ private void duplicateSelected() {
if (orig == null) return;
try { svc.addAfter(orig, orig.itemUid()); } // server returns nothing we need
catch (Exception ex) { // log but continue
- LOG.warning("Duplicate RPC failed: "+ex.getMessage());
+ logger.log(Level.WARNING, "Duplicate RPC failed: "+ex.getMessage());
}
});
@@ -285,14 +286,14 @@ private void duplicateSelected() {
stickySel = added.isEmpty() ? stickySel : List.of(added.get(0));
} catch (Exception ex) {
- LOG.warning("Refresh after duplicate failed: "+ex.getMessage());
+ logger.log(Level.WARNING, "Refresh after duplicate failed: "+ex.getMessage());
}
}
private void clearQueue() { try { svc.queueClear(); }
- catch (Exception ex) { LOG.warning("Clear failed: "+ex.getMessage()); } }
+ catch (Exception ex) { logger.log(Level.WARNING, "Clear failed: "+ex.getMessage()); } }
private void setLoopMode(boolean loop){ try { svc.queueModeSet(Map.of("loop",loop)); }
- catch (Exception ex){ LOG.warning("Loop-set failed: "+ex.getMessage()); } }
+ catch (Exception ex){ logger.log(Level.WARNING, "Loop-set failed: "+ex.getMessage()); } }
private void updateButtonStates() {
boolean connected = StatusBus.latest().get()!=null;
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanViewerController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanViewerController.java
index 1d738c0727..749e7f5bf7 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanViewerController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/RePlanViewerController.java
@@ -34,7 +34,7 @@ public class RePlanViewerController implements Initializable {
@FXML private Button copyBtn, editBtn;
private final RunEngineService svc = new RunEngineService();
- private static final Logger LOG = Logger.getLogger(RePlanViewerController.class.getName());
+ private static final Logger logger = Logger.getLogger(RePlanViewerController.class.getPackageName());
private final ObservableList parameterRows = FXCollections.observableArrayList();
private final Map> allowedPlans = new HashMap<>();
private final Map> allowedInstructions = new HashMap<>();
@@ -203,7 +203,7 @@ private void loadAllowedPlansAndInstructions() {
Platform.runLater(() -> updateWidgetState());
} catch (Exception e) {
- LOG.log(Level.WARNING, "Failed to load plans", e);
+ logger.log(Level.WARNING, "Failed to load plans", e);
}
}).start();
}
@@ -288,7 +288,7 @@ private void loadParametersForItem(QueueItem item) {
currentValue = value != null ? String.valueOf(value) : "";
isEnabled = true;
} else if (defaultValue != null) {
- currentValue = String.valueOf(defaultValue) + " (default)";
+ currentValue = String.valueOf(defaultValue);
isEnabled = false;
}
@@ -425,16 +425,16 @@ private void copyToQueue() {
var response = svc.queueItemAdd(request);
Platform.runLater(() -> {
if (!response.success()) {
- LOG.log(Level.WARNING, "Copy to queue failed", response.msg());
+ logger.log(Level.WARNING, "Copy to queue failed", response.msg());
}
});
} catch (Exception e) {
- LOG.log(Level.WARNING, "Copy to queue error", e);
+ logger.log(Level.WARNING, "Copy to queue error", e);
}
}).start();
} catch (Exception e) {
- LOG.log(Level.WARNING, "Copy to queue error", e);
+ logger.log(Level.WARNING, "Copy to queue error", e);
}
}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReQueueControlsController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReQueueControlsController.java
index 4b0e931b89..977c92dddc 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReQueueControlsController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReQueueControlsController.java
@@ -14,6 +14,7 @@
import java.net.URL;
import java.util.Map;
import java.util.ResourceBundle;
+import java.util.logging.Level;
import java.util.logging.Logger;
public final class ReQueueControlsController implements Initializable {
@@ -24,7 +25,7 @@ public final class ReQueueControlsController implements Initializable {
@FXML private Button stopBtn; // text toggles “Stop” / “Cancel Stop”
private final RunEngineService svc = new RunEngineService();
- private final Logger LOG = Logger.getLogger(getClass().getName());
+ private static final Logger logger = Logger.getLogger(ReQueueControlsController.class.getPackageName());
private volatile boolean autoEnabledSrv = false; // what server says now
private volatile boolean stopPendingSrv = false; // ditto
@@ -124,6 +125,6 @@ else if (statusObj instanceof Map,?> m) {
private void runSafely(String what, Runnable r) {
try { r.run(); }
- catch (Exception ex) { LOG.warning(what + ": " + ex); }
+ catch (Exception ex) { logger.log(Level.WARNING, what + ": " + ex); }
}
}
diff --git a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReRunningPlanController.java b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReRunningPlanController.java
index 1abc67183d..e1e5c3ed3a 100644
--- a/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReRunningPlanController.java
+++ b/app/queue-server/src/main/java/org/phoebus/applications/queueserver/controller/ReRunningPlanController.java
@@ -25,9 +25,10 @@ public final class ReRunningPlanController implements Initializable {
@FXML private TextArea planTextArea;
private final RunEngineService svc = new RunEngineService();
- private static final Logger LOG = Logger.getLogger(ReRunningPlanController.class.getName());
+ private static final Logger logger = Logger.getLogger(ReRunningPlanController.class.getPackageName());
private String lastRunningUid = "";
+ private QueueItem cachedRunningItem = null;
private final boolean viewOnly;
@@ -58,6 +59,7 @@ private void render(StatusResponse st) {
if (st == null) {
planTextArea.clear();
lastRunningUid = "";
+ cachedRunningItem = null;
copyBtn.setDisable(true);
updateBtn.setDisable(true);
return;
@@ -82,19 +84,21 @@ private void render(StatusResponse st) {
if (uid == null) { // nothing running
planTextArea.clear();
lastRunningUid = "";
+ cachedRunningItem = null;
return;
}
- QueueItem runningItem;
- if (!uid.equals(lastRunningUid)) { // new plan started
- runningItem = fetchRunningItem();
+ // Fetch running item only if it's a new plan
+ if (!uid.equals(lastRunningUid)) {
+ cachedRunningItem = fetchRunningItem();
lastRunningUid = uid;
- } else {
- runningItem = null; // keep previous text, only update run-list
}
+
+ // Always fetch the latest run list
List