Skip to content
Snippets Groups Projects
Commit 5fdc59d7 authored by Johannes Mey's avatar Johannes Mey
Browse files

simplify controller structure

parent 763cef4a
No related branches found
No related tags found
1 merge request!1festival updatesfestival updatesfestival updatesfestival updatesfestival
Pipeline #14661 failed
......@@ -9,7 +9,7 @@ plugins {
group 'de.tudresden.inf.st'
ext {
mainClassName = 'de.tudresden.inf.st.mg.Main'
mainClassName = 'de.tudresden.inf.st.mg.ImmersiveSortingController'
}
// set the main class name for `gradle run`
......
......@@ -3,9 +3,6 @@ package de.tudresden.inf.st.mg;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.protobuf.util.JsonFormat;
import de.tudresden.inf.st.ceti.Scene;
import de.tudresden.inf.st.mg.common.DiagramProvider;
import de.tudresden.inf.st.mg.common.MotionGrammarConfig;
import de.tudresden.inf.st.mg.common.MotionGrammarParser;
import de.tudresden.inf.st.mg.common.TableDeserializer;
......@@ -13,6 +10,9 @@ import de.tudresden.inf.st.mg.jastadd.model.JastAddList;
import de.tudresden.inf.st.mg.jastadd.model.RobotWorld;
import de.tudresden.inf.st.mg.jastadd.model.Table;
import de.tudresden.inf.st.mg.jastadd.model.World;
import io.javalin.Javalin;
import io.javalin.core.JavalinConfig;
import io.javalin.websocket.WsContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -21,19 +21,27 @@ import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Comparator;
import java.util.Date;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ConcurrentHashMap;
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public class ImmersiveSortingController {
public static final Path TIDY_AST_DIAGRAM_DIR = Path.of("src", "gen", "resources", "diagrams", "parsing", "tidy");
private static final Logger logger = LoggerFactory.getLogger(ImmersiveSortingController.class);
private static final Map<WsContext, String> clients = new ConcurrentHashMap<>();
private static ImmersiveSortingController INSTANCE;
private final RobotWorld world;
private ParseThread parse;
private Diagram ast;
private Diagram context;
public Main(boolean simulate) {
private boolean initialized = false;
public ImmersiveSortingController(boolean simulate) {
MotionGrammarConfig.astDiagramDir = TIDY_AST_DIAGRAM_DIR;
try {
Files.walk(TIDY_AST_DIAGRAM_DIR).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
......@@ -51,11 +59,15 @@ public class Main {
module.addDeserializer(Table.class, new TableDeserializer());
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
mapper.registerModule(module);
world.setDemonstrationTable(mapper.readValue(Main.class.getResourceAsStream("config_scene_virtual-table.yaml"), Table.class));
world.setDemonstrationTable(mapper.readValue(ImmersiveSortingController.class.getResourceAsStream("config_scene_virtual-table.yaml"), Table.class));
} catch (IOException e) {
e.printStackTrace();
}
initializeWebserver();
initialized = true;
World.enableContextPrinting(true);
world.printContext("initial");
......@@ -90,7 +102,117 @@ public class Main {
}
private ParseThread parse;
/**
* Runs the parser on the actual robot. For this to work, a robot controller must be connected to an MQTT server running
* on localhost:1883
*/
public static void main(String[] args) {
ImmersiveSortingController controller = ImmersiveSortingController.getInstance();
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ignored) {
}
}
public static ImmersiveSortingController getInstance() {
if (INSTANCE == null) {
INSTANCE = new ImmersiveSortingController(false);
}
return INSTANCE;
}
public void startParse() {
if (parse != null && parse.isAlive()) {
parse.stopParsing();
try {
parse.join();
} catch (InterruptedException ignored) {
}
}
parse = new ParseThread();
parse.start();
}
public void resetSelections() {
world.setSelectionList(new JastAddList<>());
}
public void printContext(String message) {
world.printContext(message);
}
private void initializeWebserver() {
ast = new Diagram(new Date(), 0, "<no rule>", "", "ast");
context = new Diagram(Date.from(Instant.now()), 0, "<no rule>", "", "context");
Javalin webserver = Javalin.create(JavalinConfig::enableCorsForAllOrigins).start(7070);
webserver.get("/", ctx -> ctx.result("the context is delivered at /context and the ast is delivered at /ast"));
webserver.get("/ast/", ctx -> {
ctx.json(ast);
});
webserver.get("/context/", ctx -> {
ctx.json(context);
});
webserver.ws("ast-events", ws -> {
ws.onConnect(ctx -> {
System.err.println("somebody connected " + ctx.host());
clients.put(ctx, ctx.host());
});
ws.onClose(ctx -> {
System.err.println("somebody disconnected");
clients.remove(ctx);
});
ws.onMessage(ctx -> {
if (initialized) {
logger.info("got message " + ctx.message());
} else {
logger.error("ignoring message " + ctx.message() + ", because system is not initialized yet");
return;
}
if ("parse".equals(ctx.message())) {
startParse();
} else if ("reset".equals(ctx.message())) {
resetSelections();
printContext("reset selection");
}
});
});
}
public void publish(Date timestamp, int step, String parseRule, Path diagramPath, String type) {
try {
if ("ast".equals(type)) {
ast = new Diagram(timestamp, step, parseRule, Files.readString(diagramPath), type);
clients.keySet().forEach(session -> session.send(ast));
} else if ("context".equals(type)) {
context = new Diagram(timestamp, step, parseRule, Files.readString(diagramPath), type);
clients.keySet().forEach(session -> session.send(context));
}
} catch (IOException e) {
System.err.println("Unable to read " + type + " diagram file " + diagramPath);
e.printStackTrace();
}
}
public static class Diagram {
public Date timestamp;
public int step;
public String parseRule;
public String diagram;
public String type;
public Diagram(Date timestamp, int step, String parseRule, String diagram, String type) {
this.timestamp = timestamp;
this.step = step;
this.parseRule = parseRule;
this.diagram = diagram;
this.type = type;
}
}
class ParseThread extends Thread {
......@@ -104,7 +226,7 @@ public class Main {
// parse (synchronously, long-running)
try {
var result = parser.parse();
parser.parse();
logger.info("parsing completed!");
} catch (MotionGrammarParser.ParseException e) {
throw new RuntimeException(e);
......@@ -118,36 +240,4 @@ public class Main {
}
}
public void startParse() {
if (parse != null && parse.isAlive()) {
parse.stopParsing();
try {
parse.join();
} catch (InterruptedException e) {
}
}
parse = new ParseThread();
parse.start();
}
public void resetSelections() {
world.setSelectionList(new JastAddList<>());
}
/**
* Runs the parser on the actual robot. For this to work, a robot controller must be connected to an MQTT server running
* on localhost:1883
*/
public static void main(String[] args) {
DiagramProvider webController = DiagramProvider.getInstance();
webController.setController(new Main(false));
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ignored) {
}
}
public void printContext(String message) {
world.printContext(message);
}
}
package de.tudresden.inf.st.mg.common;
import de.tudresden.inf.st.mg.Main;
import io.javalin.Javalin;
import io.javalin.core.JavalinConfig;
import io.javalin.websocket.WsContext;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class DiagramProvider {
private static DiagramProvider INSTANCE;
private Main controller;
private static final Map<WsContext, String> clients = new ConcurrentHashMap<>();
public static DiagramProvider getInstance() {
if (INSTANCE == null) {
INSTANCE = new DiagramProvider();
}
return INSTANCE;
}
private AST ast;
private AST context;
private DiagramProvider() {
ast = new AST(new Date(), 0, "<no rule>", "", "ast");
context = new AST(Date.from(Instant.now()), 0, "<no rule>", "", "context");
Javalin app = Javalin.create(JavalinConfig::enableCorsForAllOrigins).start(7070);
app.get("/", ctx -> ctx.result("the context is delivered at /context and the ast is delivered at /ast"));
app.get("/ast/", ctx -> {
ctx.json(ast);
});
app.get("/context/", ctx -> {
ctx.json(context);
});
app.ws("ast-events", ws -> {
ws.onConnect(ctx -> {
System.err.println("somebody connected " + ctx.host());
clients.put(ctx, ctx.host());
});
ws.onClose(ctx -> {
System.err.println("somebody disconnected");
clients.remove(ctx);
});
ws.onMessage(ctx -> {
System.out.println("got message " + ctx.message());
if ("parse".equals(ctx.message()) && controller != null) {
controller.startParse();
} else if ("reset".equals(ctx.message()) && controller != null) {
controller.resetSelections();
controller.printContext("reset selection");
}
});
});
}
public void publish(Date timestamp, int step, String parseRule, Path diagramPath, String type) {
try {
if ("ast".equals(type)) {
ast = new AST(timestamp, step, parseRule, Files.readString(diagramPath), type);
clients.keySet().forEach(session -> session.send(ast));
} else if ("context".equals(type)) {
context = new AST(timestamp, step, parseRule, Files.readString(diagramPath), type);
clients.keySet().forEach(session -> session.send(context));
}
} catch (IOException e) {
System.err.println("Unable to read " + type + " diagram file " + diagramPath);
e.printStackTrace();
}
}
public void setController(Main controller) {
this.controller = controller;
}
public static class AST {
public Date timestamp;
public int step;
public String parseRule;
public String diagram;
public String type;
public AST(Date timestamp, int step, String parseRule, String diagram, String type) {
this.timestamp = timestamp;
this.step = step;
this.parseRule = parseRule;
this.diagram = diagram;
this.type = type;
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment