Commit 31fcc689 authored by Andrey Filippov's avatar Andrey Filippov

Implementing quasi-parallel processing using GUI and MCP

parent 30b9d3f9
...@@ -3407,13 +3407,19 @@ public class CLTParameters { ...@@ -3407,13 +3407,19 @@ public class CLTParameters {
} }
public boolean showJDialog() { public boolean showJDialog() {
return showJDialog(false); return showJDialog(false, GenericJTabbedDialogMcp.isDefaultMcpMode());
} }
// codex 2026-01-27: optional modeless dialog for MCP/GUI co-use // codex 2026-01-27: optional modeless dialog for MCP/GUI co-use
public boolean showJDialog(boolean nonBlocking) { public boolean showJDialog(boolean nonBlocking) {
return showJDialog(nonBlocking, GenericJTabbedDialogMcp.isDefaultMcpMode());
}
// codex 2026-01-28: allow forcing GUI vs MCP mode per call
public boolean showJDialog(boolean nonBlocking, boolean useMcp) {
// GenericDialog gd = new GenericDialog("Set CLT parameters"); // GenericDialog gd = new GenericDialog("Set CLT parameters");
GenericJTabbedDialogMcp gd = new GenericJTabbedDialogMcp("Set CLT parameters",1090,900, !nonBlocking); // codex 2026-01-25 GenericJTabbedDialogMcp gd = new GenericJTabbedDialogMcp("Set CLT parameters",1090,900, !nonBlocking); // codex 2026-01-25
gd.setMcpMode(useMcp);
gd.addTab ("General", "General parameters"); gd.addTab ("General", "General parameters");
gd.addNumericField("Nominal (rectilinear) disparity between side of square cameras (pix)", this.disparity, 3,7,"pix", gd.addNumericField("Nominal (rectilinear) disparity between side of square cameras (pix)", this.disparity, 3,7,"pix",
"Used when rendering 4 images"); "Used when rendering 4 images");
......
...@@ -1197,14 +1197,23 @@ public class EyesisCorrectionParameters { ...@@ -1197,14 +1197,23 @@ public class EyesisCorrectionParameters {
public boolean showCLTBatchDialog(String title, public boolean showCLTBatchDialog(String title,
CLTParameters clt_parameters) { CLTParameters clt_parameters) {
return showCLTBatchDialog(title, clt_parameters, false); return showCLTBatchDialog(title, clt_parameters, false, GenericJTabbedDialogMcp.isDefaultMcpMode());
} }
// codex 2026-01-27: optional modeless dialog for MCP/GUI co-use // codex 2026-01-27: optional modeless dialog for MCP/GUI co-use
public boolean showCLTBatchDialog(String title, public boolean showCLTBatchDialog(String title,
CLTParameters clt_parameters, CLTParameters clt_parameters,
boolean nonBlocking) { boolean nonBlocking) {
return showCLTBatchDialog(title, clt_parameters, nonBlocking, GenericJTabbedDialogMcp.isDefaultMcpMode());
}
// codex 2026-01-28: allow forcing GUI vs MCP mode per call
public boolean showCLTBatchDialog(String title,
CLTParameters clt_parameters,
boolean nonBlocking,
boolean useMcp) {
GenericJTabbedDialogMcp gd = new GenericJTabbedDialogMcp(title,1000,1000, !nonBlocking); // codex 2026-01-25 GenericJTabbedDialogMcp gd = new GenericJTabbedDialogMcp(title,1000,1000, !nonBlocking); // codex 2026-01-25
gd.setMcpMode(useMcp);
updateAuxFromMain(); updateAuxFromMain();
...@@ -1792,7 +1801,8 @@ public class EyesisCorrectionParameters { ...@@ -1792,7 +1801,8 @@ public class EyesisCorrectionParameters {
base_path=base_path.resolve(Paths.get(dir_map.get("rootDirectory"))); base_path=base_path.resolve(Paths.get(dir_map.get("rootDirectory")));
File base_dir = new File(base_path.toString()); File base_dir = new File(base_path.toString());
if (!base_dir.exists()) { if (!base_dir.exists()) {
base_dir.mkdirs(); // codex 2026-01-28: mkdirs side-effect (consider guarding) System.out.println("Root directory "+base_path.toString()+" does not exist, ignoring "+seq_str);
return null;
} }
} }
// set sourceDirectory: // set sourceDirectory:
...@@ -1808,7 +1818,8 @@ public class EyesisCorrectionParameters { ...@@ -1808,7 +1818,8 @@ public class EyesisCorrectionParameters {
Path source_path = Paths.get(this.sourceDirectory); Path source_path = Paths.get(this.sourceDirectory);
File source_dir = new File(source_path.toString()); File source_dir = new File(source_path.toString());
if (!source_dir.exists()) { if (!source_dir.exists()) {
source_dir.mkdirs(); // codex 2026-01-28: mkdirs side-effect (consider guarding) System.out.println("Source directory "+source_path.toString()+" does not exist, ignoring "+seq_str);
return null;
} }
useCuasSeedDir = false; useCuasSeedDir = false;
...@@ -1821,7 +1832,8 @@ public class EyesisCorrectionParameters { ...@@ -1821,7 +1832,8 @@ public class EyesisCorrectionParameters {
File dir_file = new File(dir_path.toString()); File dir_file = new File(dir_path.toString());
if ((i != KEY_INDEX_UAS_LOGS) && (i != KEY_INDEX_SKY_MASK)) { // cuasUasLogs, cuasSkyMask are files, not directories if ((i != KEY_INDEX_UAS_LOGS) && (i != KEY_INDEX_SKY_MASK)) { // cuasUasLogs, cuasSkyMask are files, not directories
if (!dir_file.exists()) { if (!dir_file.exists()) {
dir_file.mkdirs(); // codex 2026-01-28: mkdirs side-effect (consider guarding) System.out.println(KEY_DIRS[i]+" directory "+dir_path.toString()+" does not exist, ignoring "+seq_str);
return null;
} }
} }
dir_string = dir_path.toString(); dir_string = dir_path.toString();
......
...@@ -12,7 +12,9 @@ import ij.IJ; ...@@ -12,7 +12,9 @@ import ij.IJ;
public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{
// codex 2026-01-25: MCP dialog capture state // codex 2026-01-25: MCP dialog capture state
private static boolean defaultMcpMode = false; private static boolean defaultMcpMode = false;
private static long dialogTimeoutMs = 30000L;
public boolean mcp_mode = false; public boolean mcp_mode = false;
private boolean mcpCanceled = false;
private final List<McpDialogField> mcpFields = new ArrayList<McpDialogField>(); private final List<McpDialogField> mcpFields = new ArrayList<McpDialogField>();
private final String dialogTitle; private final String dialogTitle;
private String currentTab = ""; private String currentTab = "";
...@@ -42,6 +44,14 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{ ...@@ -42,6 +44,14 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{
public static boolean isDefaultMcpMode() { public static boolean isDefaultMcpMode() {
return defaultMcpMode; return defaultMcpMode;
} }
// codex 2026-01-28: configurable MCP dialog submit timeout (0 = wait forever)
public static void setDialogTimeoutMs(long timeoutMs) {
dialogTimeoutMs = timeoutMs;
}
// codex 2026-01-28: override MCP mode per dialog instance
public void setMcpMode(boolean enabled) {
this.mcp_mode = enabled;
}
private void addMcpField(McpDialogField field) { private void addMcpField(McpDialogField field) {
mcpFields.add(field); mcpFields.add(field);
...@@ -121,6 +131,16 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{ ...@@ -121,6 +131,16 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{
else super.addChoice(label, items, defaultItem, tooltip, count); else super.addChoice(label, items, defaultItem, tooltip, count);
} }
@Override
public void addChoice(String label, String[] items, String defaultItem) {
addChoice(label, items, defaultItem, null, 0);
}
@Override
public void addChoice(String label, String[] items, String defaultItem, String tooltip) {
addChoice(label, items, defaultItem, tooltip, 0);
}
@Override @Override
public void addDefaultButtons() { // not used public void addDefaultButtons() { // not used
if (mcp_mode) { if (mcp_mode) {
...@@ -149,7 +169,10 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{ ...@@ -149,7 +169,10 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{
if (mcp_mode) { if (mcp_mode) {
// codex 2026-01-25: publish dialog schema to MCP registry // codex 2026-01-25: publish dialog schema to MCP registry
readIndex = 0; readIndex = 0;
McpDialogRegistry.setCurrent(new McpDialogSession(dialogTitle, mcpFields)); boolean applied = McpDialogRegistry.setCurrent(new McpDialogSession(dialogTitle, mcpFields));
if (!applied) {
System.out.println("MCP: dialog already active, ignoring \"" + dialogTitle + "\"");
}
} }
else super.buildDialog(); else super.buildDialog();
} }
...@@ -253,6 +276,17 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{ ...@@ -253,6 +276,17 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{
public boolean showDialog() { public boolean showDialog() {
if (mcp_mode) { if (mcp_mode) {
buildDialog(); buildDialog();
McpDialogSession session = McpDialogRegistry.getCurrent();
if (session != null) {
long timeout = dialogTimeoutMs;
Boolean ok = (timeout <= 0) ? session.awaitSubmit(Long.MAX_VALUE) : session.awaitSubmit(timeout);
if (ok != null && !ok.booleanValue()) {
mcpCanceled = true;
} else {
mcpCanceled = false;
}
}
McpDialogRegistry.setCurrent(null);
return true; return true;
} }
else return super.showDialog(); else return super.showDialog();
...@@ -279,7 +313,7 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{ ...@@ -279,7 +313,7 @@ public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{
@Override @Override
public boolean wasCanceled() { public boolean wasCanceled() {
if (mcp_mode) { if (mcp_mode) {
return false; return mcpCanceled;
} }
else return super.wasCanceled(); else return super.wasCanceled();
} }
......
...@@ -12,6 +12,7 @@ public class SyncCommand { ...@@ -12,6 +12,7 @@ public class SyncCommand {
public AtomicInteger stopRequested = new AtomicInteger(0); // 0 - not requested, 1 - ASAP, 2 - gracefully public AtomicInteger stopRequested = new AtomicInteger(0); // 0 - not requested, 1 - ASAP, 2 - gracefully
public String buttonLabel = ""; public String buttonLabel = "";
public boolean confirm = true; public boolean confirm = true;
public boolean fromMcp = false;
// codex 2026-01-27: MCP-controlled pause/confirm // codex 2026-01-27: MCP-controlled pause/confirm
private boolean mcpMode = false; private boolean mcpMode = false;
private boolean confirmPending = false; private boolean confirmPending = false;
......
...@@ -8,19 +8,58 @@ public class McpDialogRegistry { ...@@ -8,19 +8,58 @@ public class McpDialogRegistry {
private McpDialogRegistry() { private McpDialogRegistry() {
} }
public static void setCurrent(McpDialogSession session) { public static boolean setCurrent(McpDialogSession session) {
if (session == null) {
CURRENT.set(null);
return true;
}
McpDialogSession existing = CURRENT.get();
if (existing != null) {
return false;
}
CURRENT.set(session); CURRENT.set(session);
return true;
} }
public static McpDialogSession getCurrent() { public static McpDialogSession getCurrent() {
return CURRENT.get(); return CURRENT.get();
} }
public static void setValue(String label, String value) { public static boolean setValue(String label, String value) {
McpDialogSession session = CURRENT.get(); McpDialogSession session = CURRENT.get();
if (session == null) { if (session == null) {
return; return false;
} }
session.setValue(label, value); session.setValue(label, value);
return true;
}
public static boolean setValueForId(String id, String label, String value) {
McpDialogSession session = CURRENT.get();
if (session == null || id == null || !id.equals(session.getId())) {
return false;
}
session.setValue(label, value);
return true;
}
public static boolean submitCurrent(boolean ok) {
McpDialogSession session = CURRENT.get();
if (session == null) {
return false;
}
session.submit(ok);
CURRENT.set(null);
return true;
}
public static boolean submitForId(String id, boolean ok) {
McpDialogSession session = CURRENT.get();
if (session == null || id == null || !id.equals(session.getId())) {
return false;
}
session.submit(ok);
CURRENT.set(null);
return true;
} }
} }
...@@ -8,10 +8,13 @@ import java.util.Map; ...@@ -8,10 +8,13 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
public class McpDialogSession { public class McpDialogSession {
public static final int debugLevel = -1; // name in the same case in most other places. Compares if (debugLevel >-3)
private final String id; private final String id;
private final String title; private final String title;
private final List<McpDialogField> fields; private final List<McpDialogField> fields;
private final Map<String, String> valuesByLabel; private final Map<String, String> valuesByLabel;
private final Object submitLock = new Object();
private Boolean submittedOk = null;
public McpDialogSession(String title, List<McpDialogField> fields) { public McpDialogSession(String title, List<McpDialogField> fields) {
this.id = UUID.randomUUID().toString(); this.id = UUID.randomUUID().toString();
...@@ -37,6 +40,41 @@ public class McpDialogSession { ...@@ -37,6 +40,41 @@ public class McpDialogSession {
return; return;
} }
valuesByLabel.put(label, value); valuesByLabel.put(label, value);
if (debugLevel > -3) {
System.out.println("setValue("+label+","+value+")");
}
}
public void submit(boolean ok) {
synchronized (submitLock) {
submittedOk = Boolean.valueOf(ok);
submitLock.notifyAll();
if (debugLevel > -3) {
System.out.println("submit("+ok+")");
}
}
}
public Boolean awaitSubmit(long timeoutMs) {
synchronized (submitLock) {
if (submittedOk != null) {
return submittedOk;
}
long deadline = System.currentTimeMillis() + timeoutMs;
while (submittedOk == null) {
long remaining = deadline - System.currentTimeMillis();
if (remaining <= 0) {
break;
}
try {
submitLock.wait(remaining);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
return submittedOk;
}
} }
public String getValue(String label) { public String getValue(String label) {
......
...@@ -72,9 +72,9 @@ public class McpFsAccess { ...@@ -72,9 +72,9 @@ public class McpFsAccess {
if (configPath != null && !configPath.trim().isEmpty()) { if (configPath != null && !configPath.trim().isEmpty()) {
Path configFile = Paths.get(configPath).toAbsolutePath().normalize(); Path configFile = Paths.get(configPath).toAbsolutePath().normalize();
roots.add(configFile); roots.add(configFile);
Path configDir = configFile.getParent(); Path configDirFileParent = configFile.getParent();
if (configDir != null) { if (configDirFileParent != null) {
roots.add(configDir); roots.add(configDirFileParent);
} }
addRootsFromConfig(configFile, roots); addRootsFromConfig(configFile, roots);
} }
......
...@@ -51,6 +51,7 @@ public class McpServer { ...@@ -51,6 +51,7 @@ public class McpServer {
server.createContext("/mcp/status", new StatusHandler()); server.createContext("/mcp/status", new StatusHandler());
server.createContext("/mcp/dialog", new DialogHandler()); server.createContext("/mcp/dialog", new DialogHandler());
server.createContext("/mcp/dialog/values", new DialogValuesHandler()); server.createContext("/mcp/dialog/values", new DialogValuesHandler());
server.createContext("/mcp/dialog/submit", new DialogSubmitHandler());
server.createContext("/mcp/button", new ButtonHandler()); server.createContext("/mcp/button", new ButtonHandler());
server.createContext("/mcp/interrupt", new InterruptHandler()); server.createContext("/mcp/interrupt", new InterruptHandler());
server.createContext("/mcp/interrupt/confirm", new InterruptConfirmHandler()); server.createContext("/mcp/interrupt/confirm", new InterruptConfirmHandler());
...@@ -62,8 +63,10 @@ public class McpServer { ...@@ -62,8 +63,10 @@ public class McpServer {
server.createContext("/mcp/fs/glob", new FsGlobHandler()); server.createContext("/mcp/fs/glob", new FsGlobHandler());
server.setExecutor(null); server.setExecutor(null);
server.start(); server.start();
if (Eyesis_Correction.MCP_DEBUG_LEVEL >= Eyesis_Correction.MINIMAL_DEBUG_MCP) {
System.out.println("MCP: server started on http://127.0.0.1:" + port); System.out.println("MCP: server started on http://127.0.0.1:" + port);
} }
}
private class StatusHandler implements HttpHandler { private class StatusHandler implements HttpHandler {
@Override @Override
...@@ -89,14 +92,40 @@ public class McpServer { ...@@ -89,14 +92,40 @@ public class McpServer {
return; return;
} }
Map<String, String> params = parseParams(exchange); Map<String, String> params = parseParams(exchange);
String id = params.get("id");
String label = params.get("label"); String label = params.get("label");
String value = params.get("value"); String value = params.get("value");
if (label == null) { if (label == null) {
sendJson(exchange, 400, "{\"ok\":false,\"error\":\"Missing label\"}"); sendJson(exchange, 400, "{\"ok\":false,\"error\":\"Missing label\"}");
return; return;
} }
McpDialogRegistry.setValue(label, value); boolean applied = (id == null) ? McpDialogRegistry.setValue(label, value) : McpDialogRegistry.setValueForId(id, label, value);
if (applied) {
if (Eyesis_Correction.MCP_DEBUG_LEVEL >= Eyesis_Correction.MINIMAL_DEBUG_MCP) {
System.out.println("MCP: dialog value label=\"" + label + "\"");
}
sendJson(exchange, 200, "{\"ok\":true}"); sendJson(exchange, 200, "{\"ok\":true}");
} else {
sendJson(exchange, 409, "{\"ok\":false,\"error\":\"No active dialog or id mismatch\"}");
}
}
}
private class DialogSubmitHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
sendJson(exchange, 405, "{\"ok\":false,\"error\":\"POST required\"}");
return;
}
Map<String, String> params = parseParams(exchange);
String id = params.get("id");
boolean ok = parseBool(params.get("ok"), true);
boolean applied = (id == null) ? McpDialogRegistry.submitCurrent(ok) : McpDialogRegistry.submitForId(id, ok);
if (applied && Eyesis_Correction.MCP_DEBUG_LEVEL >= Eyesis_Correction.MINIMAL_DEBUG_MCP) {
System.out.println("MCP: dialog submit ok=" + ok);
}
sendJson(exchange, 200, applied ? "{\"ok\":true}" : "{\"ok\":false,\"error\":\"No active dialog\"}");
} }
} }
...@@ -113,7 +142,10 @@ public class McpServer { ...@@ -113,7 +142,10 @@ public class McpServer {
sendJson(exchange, 400, "{\"ok\":false,\"error\":\"Missing label\"}"); sendJson(exchange, 400, "{\"ok\":false,\"error\":\"Missing label\"}");
return; return;
} }
owner.triggerCommand(label); owner.triggerCommand(label, true);
if (Eyesis_Correction.MCP_DEBUG_LEVEL >= Eyesis_Correction.MINIMAL_DEBUG_MCP) {
System.out.println("MCP: button \"" + label + "\"");
}
sendJson(exchange, 200, "{\"ok\":true}"); sendJson(exchange, 200, "{\"ok\":true}");
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment