Skip to content
Snippets Groups Projects
Commit ce746b07 authored by René Schöne's avatar René Schöne
Browse files

moving coordinator to new repo

parent 48f81a79
No related branches found
No related tags found
1 merge request!1Multiple scenes, multiple robots and more
Pipeline #9859 passed
build
src/gen-res/
src/gen/
out/
*.class
buildscript {
repositories.mavenCentral()
dependencies {
classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3'
}
}
plugins {
id 'ros3rag.java-application-conventions'
id 'ros3rag.java-ragconnect-conventions'
}
mainClassName = 'de.tudresden.inf.st.coordinator.MainCoordinator'
dependencies {
}
ext.ragConnectInputGrammar = 'src/main/jastadd/Coordinator.relast'
ext.ragConnectInputConnect = 'src/main/jastadd/Coordinator.connect'
ext.ragConnectRootNode = 'Coordinator'
ext.relastFiles = ["src/gen/jastadd/Coordinator.relast", "src/gen/jastadd/RagConnect.relast"]
ext.jastaddAstPackage = 'de.tudresden.inf.st.coordinator.ast'
apply plugin: 'jastadd'
dependencies {
// jastadd2 "org.jastadd:jastadd:2.3.5"
jastadd2 fileTree(include: ['jastadd2.jar'], dir: '../libs')
api group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15'
implementation project(':ros3rag.common')
}
sourceCompatibility = 11
targetCompatibility = 11
// phases: ragConnect -> RelAst -> JastAdd
// phase: ragConnect
task ragConnect(type: JavaExec) {
group = 'Build'
main = 'org.jastadd.ragconnect.compiler.Compiler'
classpath = configurations.ragconnectClasspath
args([
'--o=src/gen/jastadd',
project.ext.ragConnectInputGrammar,
project.ext.ragConnectInputConnect,
'--logReads',
'--logWrites',
// '--verbose',
'--rootNode=' + project.ext.ragConnectRootNode,
'--incremental=param',
"--tracing=cache,flush"
])
}
// Input files for relast
//def relastFiles = ["src/gen/jastadd/MinimalModel.relast", "src/gen/jastadd/RagConnect.relast"]
task grammar2uml(type: JavaExec) {
main = 'de.tudresden.inf.st.jastadd.grammar2uml.compiler.Compiler'
classpath = configurations.grammar2umlClasspath
args([
'--verbose',
] + project.ext.relastFiles)
}
// phase: RelAst
task relastToJastAdd(type: JavaExec) {
group = 'Build'
main = "-jar"
args(["../libs/relast.jar",
"--grammarName=./src/gen/jastadd/model",
"--useJastAddNames",
"--listClass=java.util.ArrayList",
"--jastAddList=JastAddList",
"--serializer=jackson",
"--resolverHelper",
"--file"
] + project.ext.relastFiles)
inputs.files project.ext.relastFiles
outputs.files file("./src/gen/jastadd/model.ast"), file("./src/gen/jastadd/model.jadd")
}
// phase: JastAdd
jastadd {
configureModuleBuild()
modules {
//noinspection GroovyAssignabilityCheck
module("coordinator") {
// java {
// basedir "src/"
// include "main/**/*.java"
// include "gen/**/*.java"
// }
jastadd {
basedir "src/"
include "main/jastadd/**/*.ast"
include "main/jastadd/**/*.jadd"
include "main/jastadd/**/*.jrag"
include "gen/jastadd/**/*.ast"
include "gen/jastadd/**/*.jadd"
include "gen/jastadd/**/*.jrag"
}
scanner {
include "src/main/jastadd/Coordinator.flex"
}
parser {
include "src/main/jastadd/Coordinator.parser"
}
}
}
cleanGen.doFirst {
delete "src/gen/java/de"
delete "src/gen-res/BuildInfo.properties"
}
module = "coordinator"
astPackage = project.ext.jastaddAstPackage
genDir = 'src/gen/java'
buildInfoDir = 'src/gen-res'
parser.name = 'CoordinatorParser'
scanner.genDir = "src/gen/java/de/tudresden/inf/st/coordinator/scanner"
parser.genDir = "src/gen/java/de/tudresden/inf/st/coordinator/parser"
// jastaddOptions = ["--lineColumnNumbers", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
// default options are: '--rewrite=cnta', '--safeLazy', '--visitCheck=false', '--cacheCycle=false'
extraJastAddOptions = [
'--lineColumnNumbers',
'--List=JastAddList',
'--cache=all',
"--flush=api",
"--incremental=param,debug",
"--tracing=cache,flush",
]
}
cleanGen.doFirst {
delete "src/gen/jastadd"
}
// Workflow configuration for phases
generateAst.dependsOn relastToJastAdd
relastToJastAdd.dependsOn ragConnect
receive Component.IncomingStatus ;
//send tree Coordinator.NextComponentToStart using NameOfComponent ;
//
//NameOfComponent maps Component c to String {:
// return c.getName();
//:}
send Component.NextCommand using CommandCheck ;
CommandCheck maps String command to String {:
if (command == null) {
reject();
}
return command;
:}
package de.tudresden.inf.st.coordinator.scanner;
import de.tudresden.inf.st.coordinator.parser.CoordinatorParser.Terminals;
%%
// define the signature for the generated scanner
%public
%final
%class CoordinatorScanner
%extends beaver.Scanner
// the interface between the scanner and the parser is the nextToken() method
%type beaver.Symbol
%function nextToken
%yylexthrow beaver.Scanner.Exception
// store line and column information in the tokens
%line
%column
// this code will be inlined in the body of the generated scanner class
%{
private beaver.Symbol sym(short id) {
return new beaver.Symbol(id, yyline + 1, yycolumn + 1, yylength(), yytext());
}
private beaver.Symbol symText(short id) {
return new beaver.Symbol(id, yyline + 1, yycolumn + 1, yylength(), yytext().substring(1, yytext().length() - 1));
}
%}
WhiteSpace = [ ] | \t | \f | \n | \r | \r\n
Identifier = [:jletter:][:jletterdigit:]*
Text = \" ([^\"]*) \"
Comment = "//" [^\n\r]+
%%
// discard whitespace information and comments
{WhiteSpace} { }
{Comment} { }
// ** token definitions **
// Begin of line with capital letter
"components" { return sym(Terminals.COMPONENTS); }
"component" { return sym(Terminals.COMPONENT); }
"docker compose" { return sym(Terminals.DOCKER_COMPOSE); }
"mqtt topic" { return sym(Terminals.MQTT_TOPIC); }
"=" { return sym(Terminals.EQUALS); }
"," { return sym(Terminals.COMMA); }
"<" { return sym(Terminals.LT); }
"{" { return sym(Terminals.LB_CURLY); }
"}" { return sym(Terminals.RB_CURLY); }
{Identifier} { return sym(Terminals.NAME); }
{Text} { return symText(Terminals.TEXT); }
<<EOF>> { return sym(Terminals.EOF); }
/* error fallback */
[^] { throw new Error("Illegal character '"+ yytext() +"' at line " + (yyline+1) + " column " + (yycolumn+1) + " in state " + yystate()); }
import java.util.*;
aspect Computation {
syn String Component.getNextCommand() {
if (!getIncomingStatus().equals("up")) {
System.out.println(getName() + " not up");
// component is not "up" yet
return null;
}
for (Component predecessor : getPredecessorList()) {
if (!predecessor.getIncomingStatus().equals("ready")) {
// one of required component is not "ready" yet
System.out.println(getName() + " missing " + predecessor.getName());
return null;
}
}
// all required components are "ready", and this component is "up"
System.out.println(getName() + " ready to be started");
return "start";
}
}
aspect Manipulation {
public static boolean Coordinator.DRY_RUN = false;
public Set<Component> Coordinator.getRunningComponents() throws IOException, InterruptedException {
String[] args = { "docker-compose", "ps", "--services", "--filter", "status=running" };
if (DRY_RUN) {
System.out.println("Would start > " + java.util.Arrays.toString(args));
return Collections.emptySet();
}
ProcessBuilder builder = new ProcessBuilder(args);
Process process = builder.start();
process.waitFor();
if (process.exitValue() != 0) {
System.err.println("Could not list services.");
return Collections.emptySet();
}
Set<Component> result = new HashSet<>();
List<String> services;
try (java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getInputStream()))) {
services = reader.lines().collect(java.util.stream.Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
return Collections.emptySet();
}
for (String service : services) {
resolveComponentByDockerComposeName(service).ifPresentOrElse(
comp -> result.add(comp),
() -> System.err.println("Could not resolve component for '" + service + "'!")
);
}
return result;
}
public boolean Component.callDockerCompose() throws IOException, InterruptedException {
String[] args = { "docker-compose", "up", "-d", getDockerComposeName() };
if (coordinator().DRY_RUN) {
System.out.println("Would start > " + java.util.Arrays.toString(args));
return true;
}
ProcessBuilder builder = new ProcessBuilder(args);
Process process = builder.start();
process.waitFor();
return process.exitValue() == 0;
}
}
aspect Navigation {
inh Coordinator Component.coordinator();
inh Coordinator ParsedPrecedenceRelation.coordinator();
eq Coordinator.getChild().coordinator() = this;
}
aspect Printing {
syn String Coordinator.prettyPrint() {
StringBuilder sb = new StringBuilder();
for (Component comp : getComponentList()) {
sb.append(comp.prettyPrint()).append("\n");
}
return sb.toString();
}
syn String Component.prettyPrint() {
return "<Name: " + getName() + ", DockerComposeName: " + getDockerComposeName() + ", MqttTopicPrefix: " + getMqttTopicPrefix() + ", IncomingStatus: " + getIncomingStatus() + ", NextCommand: " + getNextCommand() + ">";
}
syn String ParsedPrecedenceRelation.prettyPrint() {
StringBuilder sb = new StringBuilder();
StringJoiner sjPred = new StringJoiner(", ");
getPredecessorList().forEach(pred -> sjPred.add(pred.getName()));
StringJoiner sjSucc = new StringJoiner(", ");
getSuccessorList().forEach(succ -> sjSucc.add(succ.getName()));
sb.append(sjPred.toString()).append(" < ").append(sjSucc.toString());
return sb.toString();
}
}
aspect Resolving {
syn Optional<Component> Coordinator.resolveComponent(String name) {
for (Component comp : getComponentList()) {
if (comp.getName().equals(name)) {
return Optional.of(comp);
}
}
return Optional.empty();
}
syn Optional<Component> Coordinator.resolveComponentByDockerComposeName(String dockerComposeName) {
for (Component comp : getComponentList()) {
if (comp.getDockerComposeName().equals(dockerComposeName)) {
return Optional.of(comp);
}
}
return Optional.empty();
}
refine RefResolverStubs eq ParsedPrecedenceRelation.resolvePredecessorByToken(String id, int position) {
return coordinator().resolveComponent(id).orElseThrow(() -> new RuntimeException("Predecessor '" + id + "' not found in " + this.prettyPrint() + "!"));
}
refine RefResolverStubs eq ParsedPrecedenceRelation.resolveSuccessorByToken(String id, int position) {
return coordinator().resolveComponent(id).orElseThrow(() -> new RuntimeException("Successor '" + id + "' not found in " + this.prettyPrint() + "!"));
}
}
aspect AdditionalTypes {
public class ReversedList<T> extends beaver.Symbol implements Iterable<T> {
private java.util.Deque<T> delegatee = new java.util.ArrayDeque<>();
public java.util.Iterator<T> iterator() {
return delegatee.descendingIterator();
}
public void add(T t) {
delegatee.add(t);
}
}
public class StringList extends ReversedList<String> {}
}
%header {:
package de.tudresden.inf.st.coordinator.parser;
import de.tudresden.inf.st.coordinator.ast.*;
import java.util.Map;
import java.util.HashMap;
:} ;
%embed {:
private static <T extends ASTNode<?>> void insertZero(JastAddList<T> listNode, T child) {
listNode.insertChild(child, 0);
}
private void replaceRelations(Coordinator o) {
for (ParsedPrecedenceRelation rel : o.getParsedPrecedenceRelationList()) {
for (Component pred : rel.getPredecessorList()) {
for (Component succ : rel.getSuccessorList()) {
pred.addSuccessor(succ);
}
}
}
}
:} ;
%goal goal;
Coordinator goal =
COMPONENTS LB_CURLY coordinator_body.o RB_CURLY {: o.treeResolveAll(); replaceRelations(o); return o; :}
;
Coordinator coordinator_body =
component.c coordinator_body.o {: o.addComponent(c); return o; :}
| string_list.preds LT string_list.succs coordinator_body.o
{:
ParsedPrecedenceRelation rel = new ParsedPrecedenceRelation();
o.addParsedPrecedenceRelation(rel);
preds.forEach(pred -> rel.addPredecessor(Component.createRef(pred)));
succs.forEach(succ -> rel.addPredecessor(Component.createRef(succ)));
return o;
:}
| {: return new Coordinator(); :}
;
Component component =
COMPONENT NAME.name COMMA DOCKER_COMPOSE EQUALS TEXT.dc COMMA MQTT_TOPIC EQUALS TEXT.mqtt
{:
Component result = new Component();
result.setName(name);
result.setDockerComposeName(dc);
result.setMqttTopicPrefix(mqtt);
return result;
:}
;
StringList string_list =
NAME.n COMMA string_list.slb {: slb.add(n); return slb; :}
| NAME.n {: StringList slb = new StringList(); slb.add(n); return slb; :}
;
Coordinator ::= Component* ParsedPrecedenceRelation* ; // /NextComponentToStart:Component/ ;
Component ::= <Name:String> <DockerComposeName:String> <MqttTopicPrefix:String> <IncomingStatus:String> /<NextCommand:String>/ ;
rel Component.Predecessor* <-> Component.Successor* ;
ParsedPrecedenceRelation ;
rel ParsedPrecedenceRelation.Predecessor* -> Component ;
rel ParsedPrecedenceRelation.Successor* -> Component ;
package de.tudresden.inf.st.coordinator;
import beaver.Parser;
import de.tudresden.inf.st.coordinator.ast.Component;
import de.tudresden.inf.st.coordinator.ast.Coordinator;
import de.tudresden.inf.st.coordinator.ast.MqttHandler;
import de.tudresden.inf.st.coordinator.parser.CoordinatorParser;
import de.tudresden.inf.st.coordinator.scanner.CoordinatorScanner;
import de.tudresden.inf.st.ros3rag.common.Util;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Entrypoint for Coordinator.
*
* @author rschoene - Initial contribution
*/
public class MainCoordinator {
private static final String TOPIC_EXIT = "coordinator/exit";
private static final String TOPIC_MODEL = "coordinator/model";
private static final String TOPIC_STATUS = "coordinator/status";
private static final String TOPIC_READY = "coordinator/ready";
private Coordinator coordinator;
private MqttHandler mainHandler;
private Component rosCore;
private Component mosquitto;
public static void main(String[] args) throws Exception {
MainCoordinator main = new MainCoordinator();
// main.manuallyBuild();
main.parsedBuild();
if (Arrays.asList(args).contains("--dry-run")) {
Coordinator.DRY_RUN = true;
}
main.start();
}
@SuppressWarnings("unused")
private void manuallyBuild() {
coordinator = new Coordinator();
Component robotCtrlA = newComponent("Robot Control A", "ros_place_a", "ros-place-a");
Component robotCtrlB = newComponent("Robot Control B", "ros_place_b", "ros-place-b");
Component ragA = newComponent("RAG A", "rag_place_a", "rag-a");
Component ragB = newComponent("RAG B", "rag_place_b", "rag-b");
Component random = newComponent("Random Dummy", "cgv_random_dummy", "random");
rosCore = newComponent("ROS core", "ros_core", "");
mosquitto = newComponent("MQTT broker", "mosquitto", "");
coordinator.addComponent(robotCtrlA);
coordinator.addComponent(robotCtrlB);
coordinator.addComponent(ragA);
coordinator.addComponent(ragB);
coordinator.addComponent(random);
coordinator.addComponent(rosCore);
coordinator.addComponent(mosquitto);
// ros core needed for all other ros nodes
rosCore.addSuccessor(robotCtrlA);
rosCore.addSuccessor(robotCtrlB);
rosCore.addSuccessor(random);
// mqtt broker needed for all robot ctrl and rag nodes
mosquitto.addSuccessor(robotCtrlA);
mosquitto.addSuccessor(robotCtrlB);
mosquitto.addSuccessor(ragA);
mosquitto.addSuccessor(ragB);
// robot control needs to start before rag
robotCtrlA.addSuccessor(ragA);
robotCtrlB.addSuccessor(ragB);
// rag place B needs to start before rag place A
ragB.addSuccessor(ragA);
// random should start last
robotCtrlA.addSuccessor(random);
robotCtrlB.addSuccessor(random);
ragA.addSuccessor(random);
ragB.addSuccessor(random);
}
private void parsedBuild() throws IOException, Parser.Exception {
Reader in = Files.newBufferedReader(Util.pathToModuleDirectory("ros3rag.coordinator")
.resolve("src/main/resources/ros3rag.coordinator"));
CoordinatorScanner scanner = new CoordinatorScanner(in);
CoordinatorParser parser = new CoordinatorParser();
coordinator = (Coordinator) parser.parse(scanner);
for (Component comp : coordinator.getComponentList()) {
if (comp.getName().equals("rosCore")) {
rosCore = comp;
}
if (comp.getName().equals("mosquitto")) {
mosquitto = comp;
}
}
in.close();
}
private void start() throws Exception {
final String mqttHost = "localhost";
coordinator.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
for (Component comp : coordinator.getComponentList()) {
comp.connectIncomingStatus("mqtt://" + mqttHost + "/" + comp.getMqttTopicPrefix() + "/status");
comp.connectNextCommand("mqtt://" + mqttHost + "/" + comp.getMqttTopicPrefix() + "/command", false);
}
Set<Component> alreadyRunning = coordinator.getRunningComponents();
for (Component comp : coordinator.getComponentList()) {
if (!alreadyRunning.contains(comp)) {
comp.callDockerCompose();
}
}
mainHandler = new MqttHandler().dontSendWelcomeMessage();
mainHandler.setHost(mqttHost);
mainHandler.waitUntilReady(2, TimeUnit.SECONDS);
CountDownLatch exitCondition = new CountDownLatch(1);
mainHandler.newConnection(TOPIC_EXIT, bytes -> exitCondition.countDown());
mainHandler.newConnection(TOPIC_MODEL, bytes -> this.printStatus());
mainHandler.newConnection(TOPIC_READY, bytes -> this.setReady());
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
// wait some time for ros_core and mqtt_broker to be started
TimeUnit.SECONDS.sleep(2);
setReady();
exitCondition.await();
}
private void setReady() {
// set ros-core and mqtt-broker to ready
rosCore.setIncomingStatus("ready");
mosquitto.setIncomingStatus("ready");
}
private void printStatus() {
String content = coordinator.prettyPrint();
System.out.println(content);
if (mainHandler != null) {
mainHandler.publish(TOPIC_STATUS, content.getBytes(StandardCharsets.UTF_8));
}
}
private void close() {
if (coordinator != null) {
coordinator.ragconnectCloseConnections();
}
if (mainHandler != null) {
mainHandler.close();
}
}
private static Component newComponent(String name, String dockerComposeName, String mqttTopicPrefix) {
Component result = new Component();
result.setName(name);
result.setDockerComposeName(dockerComposeName);
result.setMqttTopicPrefix(mqttTopicPrefix);
return result;
}
}
components {
component robotCtrlA, docker compose = "ros_place_a", mqtt topic = "ros-place-a"
component robotCtrlB, docker compose = "ros_place_b", mqtt topic = "ros-place-b"
component ragA, docker compose = "rag_place_a", mqtt topic = "rag-a"
component ragB, docker compose = "rag_place_b", mqtt topic = "rag-b"
component random, docker compose = "cgv_random_dummy", mqtt topic = "random"
component rosCore, docker compose = "ros_core", mqtt topic = ""
component mosquitto, docker compose = "mosquitto", mqtt topic = ""
// ros core needed for all other ros nodes
rosCore < robotCtrlA, robotCtrlB, random
// mqtt broker needed for all robot ctrl and rag nodes
mosquitto < robotCtrlA, robotCtrlB, ragA, ragB
// robot control needs to start before rag
robotCtrlA < ragA
robotCtrlB < ragB
// rag place B needs to start before rag place A
ragB < ragA
// random should start last
robotCtrlA, robotCtrlB, ragA, ragB < random
}
...@@ -3,7 +3,6 @@ rootProject.name = 'ros3rag' ...@@ -3,7 +3,6 @@ rootProject.name = 'ros3rag'
include 'ros3rag.placeA' include 'ros3rag.placeA'
include 'ros3rag.placeB' include 'ros3rag.placeB'
include 'ros3rag.common' include 'ros3rag.common'
include 'ros3rag.coordinator'
// include 'ros3rag.senderstub' // include 'ros3rag.senderstub'
// include 'ros3rag.receiverstub' // include 'ros3rag.receiverstub'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment