diff --git a/build.gradle b/build.gradle index 95df5f5ca49ab6597d4f6c2c2d2a97e9cc1aaf20..4f8a10197f3c80bba9cd79f148c31900ca9390e6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,60 @@ -plugins { - id 'java' +// General configuration (plugins, settings, dependencies) +buildscript { + repositories.mavenCentral() + } -group 'org.example' -version '1.0-SNAPSHOT' +group 'de.tudresden.inf.st' +version '0.1' + +apply plugin: 'java' +apply plugin: 'java-library' +apply plugin: 'application' +apply plugin: "idea" + +java.toolchain.languageVersion.set(JavaLanguageVersion.of(11)) repositories { + jcenter() mavenCentral() } dependencies { + + // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.3' + + compile fileTree(include: ['pnml-relast-engine-fatjar-0.1.jar'], dir: './libs') + testCompile group: 'junit', name: 'junit', version: '4.12' } + +jar { + manifest { + attributes "Main-Class": "de.tudresden.inf.st.sample.Main" + } + + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } + + exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA' + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + archiveBaseName = 'pnml-pkg-sample' + +} + +task fatJar(type: Jar) { + dependsOn jar + group = "build" + + archiveAppendix = "fatjar" + from sourceSets.main.output + from { + configurations.runtimeClasspath.collect {it.isDirectory() ? it : zipTree(it) } + } + + manifest.attributes "Main-Class": "${mainClassName}" +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 62d4c053550b91381bbd28b1afc82d634bf73a8a..d9b7505b1096bc428abaa017cb79c9d2da247111 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4b4429748d92848a3d820c7b099fbeb941066ae..19869d6658eca905211de6b30978e19634ac3931 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip diff --git a/libs/pnml-relast-engine-fatjar-0.1.jar b/libs/pnml-relast-engine-fatjar-0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..06f20b04c51a4af521214389dc8ee0a206229fab Binary files /dev/null and b/libs/pnml-relast-engine-fatjar-0.1.jar differ diff --git a/src/main/java/de/tudresden/inf/st/sample/Main.java b/src/main/java/de/tudresden/inf/st/sample/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..ec7db71247cd951208b8d763291961158ed33dfe --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/Main.java @@ -0,0 +1,116 @@ +package de.tudresden.inf.st.sample; + +import de.tudresden.inf.st.pnml.engine.execution.TransitionCallback; +import de.tudresden.inf.st.pnml.engine.execution.TransitionCallbackService; +import de.tudresden.inf.st.pnml.engine.ros.DiNeRosNode; +import de.tudresden.inf.st.pnml.engine.transform.PetriNetInitializer; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonCallbackStorage; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonMarking; +import de.tudresden.inf.st.pnml.jastadd.model.PetriNet; +import de.tudresden.inf.st.pnml.jastadd.model.PnmlParser; +import de.tudresden.inf.st.sample.callback.AbstractNetCallback; +import de.tudresden.inf.st.sample.callback.CallbackMapper; +import de.tudresden.inf.st.sample.communication.Node2Subscriber1; +import de.tudresden.inf.st.sample.nodes.Node1; +import de.tudresden.inf.st.sample.nodes.Node2; +import org.ros.node.DefaultNodeMainExecutor; +import org.ros.node.NodeConfiguration; +import org.ros.node.NodeMainExecutor; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * <Generated> + */ +public class Main { + + // actual ros / mqtt host url is inserted based on configuration + private static final String ROS_HOST = "localhost"; + private static final String ROS_MASTER_URI = "http://localhost:11311"; + private static final String MQTT_HOST = "localhost"; + + private static final NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(ROS_HOST); + private static final NodeMainExecutor nodeMainExecutor = DefaultNodeMainExecutor.newDefault(); + + public static void main(java.lang.String[] args) throws IOException, ParserConfigurationException, SAXException { + + /* + * <Generated> + * Petri Net base data initialization + */ + + // actual petri net paths are generated + String[] petriNetPaths = {"/src/main/resources/nets/publisher-net.pnml", "/src/main/resources/nets/subscriber-net.pnml"}; + List<PetriNet> petriNets = new ArrayList<>(); + Map<String, BalloonMarking> markings = new HashMap<>(); + Map<String, BalloonCallbackStorage> callbackStorages = new HashMap<>(); + + for(String petriNetPath : petriNetPaths){ + petriNets.add(PnmlParser.parsePnml(petriNetPath).get(0)); + } + + TransitionCallbackService transitionCallbackService = TransitionCallbackService.getInstance(); + + for(PetriNet petriNet : petriNets){ + markings.put(petriNet.getId(), petriNet.initializeBalloonMarking()); + BalloonCallbackStorage storage = petriNet.initializeCallbackStorage(); + transitionCallbackService.init(petriNet.getId(), storage); + callbackStorages.put(petriNet.getId(), storage); + PetriNetInitializer.initInputSignalConnections(petriNet, MQTT_HOST, "mqtt"); + PetriNetInitializer.initOutputSignalConnections(petriNet, MQTT_HOST, "mqtt"); + } + + /* + * <Generated> + * Callback and token integration + */ + + Map<String, List<AbstractNetCallback>> callbackMap1 = CallbackMapper.initCallbacksForNode1(petriNets.get(0)); + + for (Map.Entry<String, List<AbstractNetCallback>> entry : callbackMap1.entrySet()) { + transitionCallbackService.registerCallback(petriNets.get(0), entry.getKey(), (TransitionCallback) entry.getValue()); + } + + Map<String, List<AbstractNetCallback>> callbackMap2 = CallbackMapper.initCallbacksForNode2(petriNets.get(1)); + + for (Map.Entry<String, List<AbstractNetCallback>> entry : callbackMap2.entrySet()) { + transitionCallbackService.registerCallback(petriNets.get(1), entry.getKey(), (TransitionCallback) entry.getValue()); + } + + /* + * <Generated> + * Start of the Dineros NodescallbackMap1 + */ + + nodeConfiguration.setMasterUri(URI.create(ROS_MASTER_URI)); + + // generated + DiNeRosNode node1 = new Node1("node1", petriNets.get(0), + markings.get(petriNets.get(0).getId()), callbackStorages.get(petriNets.get(0).getId())); + DiNeRosNode node2 = new Node2("node2", petriNets.get(1), + markings.get(petriNets.get(1).getId()), callbackStorages.get(petriNets.get(1).getId())); + + /* + DiNeRosNode diNeRosNode = new DiNeRosDefaultNode("TestNode", petriNets.get(0), + markings.get(petriNets.get(0).getId()), callbackStorages.get(petriNets.get(0).getId())); + diNeRosNode.getDinerosServiceServers().put("locB", new DiNeRosDefaultServer()); + */ + + node2.getDinerosSubscribers().put("locB", new Node2Subscriber1()); + + new Thread(() -> nodeMainExecutor.execute(node1, nodeConfiguration)) {{ + start(); + }}; + + new Thread(() -> nodeMainExecutor.execute(node2, nodeConfiguration)) {{ + start(); + }}; + } +} diff --git a/src/main/java/de/tudresden/inf/st/sample/balloon/Token.java b/src/main/java/de/tudresden/inf/st/sample/balloon/Token.java new file mode 100644 index 0000000000000000000000000000000000000000..43cbf6c728144764ab3fde6001c2447882c0237d --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/balloon/Token.java @@ -0,0 +1,28 @@ +package de.tudresden.inf.st.sample.balloon; + +import java.util.ArrayList; +import java.util.List; + +// input file provide by user +public class Token { + + private String field; + + private List<String> fieldList = new ArrayList<>(); + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public List<String> getFieldList() { + return fieldList; + } + + public void setFieldList(List<String> fieldList) { + this.fieldList = fieldList; + } +} diff --git a/src/main/java/de/tudresden/inf/st/sample/callback/AbstractNetCallback.java b/src/main/java/de/tudresden/inf/st/sample/callback/AbstractNetCallback.java new file mode 100644 index 0000000000000000000000000000000000000000..96804024fc4b51c98d915e71786432e5b818aa8b --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/callback/AbstractNetCallback.java @@ -0,0 +1,63 @@ +package de.tudresden.inf.st.sample.callback; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.tudresden.inf.st.pnml.engine.execution.TransitionCallback; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonToken; +import de.tudresden.inf.st.sample.balloon.Token; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +// only the token class is generated, this is a static class +// this class gets extended by the user for concrete callbacks +// this class hides (de-)serialisation of the tokens +public abstract class AbstractNetCallback extends TransitionCallback { + + public AbstractNetCallback(String id, int priority, List<String> params) { + super(id, priority, params); + } + + public AbstractNetCallback(String id, int priority) { + super(id, priority); + } + + private Token parseToken(String s) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(s, Token.class); + } + + public abstract void processTokenCallback(List<Token> tokens); + + @Override + public final List<BalloonToken> processToken(List<BalloonToken> tokens) { + + List<Token> resultTokens = new ArrayList<>(); + List<BalloonToken> resultBalloonTokens = new ArrayList<>(); + ObjectMapper mapper = new ObjectMapper(); + + for(BalloonToken bt : tokens){ + try { + Token t = parseToken(bt.getValue()); + resultTokens.add(t); + } catch (IOException e) { + e.printStackTrace(); + } + } + + processTokenCallback(resultTokens); + + for(Token t : resultTokens){ + BalloonToken newBt = new BalloonToken(); + try { + newBt.setValue(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(t)); + resultBalloonTokens.add(newBt); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + return resultBalloonTokens; + } +} diff --git a/src/main/java/de/tudresden/inf/st/sample/callback/CallbackMapper.java b/src/main/java/de/tudresden/inf/st/sample/callback/CallbackMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..3f4a28f777f234c100ae7593b37f7625ec59b884 --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/callback/CallbackMapper.java @@ -0,0 +1,33 @@ +package de.tudresden.inf.st.sample.callback; + +import de.tudresden.inf.st.pnml.jastadd.model.PetriNet; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * <Generated> + */ +public class CallbackMapper { + + // methods generated for each petri net + public static final Map<String, List<AbstractNetCallback>> initCallbacksForNode1(PetriNet petriNet){ + + Map<String, List<AbstractNetCallback>> callbackMap = new HashMap<>(); + + // TODO: implementation by user + + return callbackMap; + } + + // methods generated for each petri net + public static final Map<String, List<AbstractNetCallback>> initCallbacksForNode2(PetriNet petriNet){ + + Map<String, List<AbstractNetCallback>> callbackMap = new HashMap<>(); + + // TODO: implementation by user + + return callbackMap; + } +} diff --git a/src/main/java/de/tudresden/inf/st/sample/communication/AbstractDinerosServer.java b/src/main/java/de/tudresden/inf/st/sample/communication/AbstractDinerosServer.java new file mode 100644 index 0000000000000000000000000000000000000000..f62980758f63624cc93a6d1c25f118a15e5689a3 --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/communication/AbstractDinerosServer.java @@ -0,0 +1,25 @@ +package de.tudresden.inf.st.sample.communication; + +import de.tudresden.inf.st.pnml.engine.ros.DiNeRosNodeUtil; +import de.tudresden.inf.st.pnml.engine.ros.DiNeRosServer; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonCallbackStorage; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonMarking; +import de.tudresden.inf.st.pnml.jastadd.model.PetriNet; + +public abstract class AbstractDinerosServer implements DiNeRosServer { + + @Override + public final boolean execute(BalloonMarking balloonMarking, BalloonCallbackStorage balloonCallbackStorage, PetriNet petriNet, String s) { + + boolean success = false; + + // this mechanism enforces the user to leave a server instance with a clean state (see server def) + while (DiNeRosNodeUtil.hasEnabledTransition(petriNet, s, balloonMarking)) { + success = serve(balloonMarking, balloonCallbackStorage, petriNet, s); + } + + return success; + } + + public abstract boolean serve(BalloonMarking balloonMarking, BalloonCallbackStorage balloonCallbackStorage, PetriNet petriNet, String subnet); +} diff --git a/src/main/java/de/tudresden/inf/st/sample/communication/AbstractDinerosSubscriber.java b/src/main/java/de/tudresden/inf/st/sample/communication/AbstractDinerosSubscriber.java new file mode 100644 index 0000000000000000000000000000000000000000..fef2e51ed54351c78fb5c976b3613fdcc979ea6c --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/communication/AbstractDinerosSubscriber.java @@ -0,0 +1,24 @@ +package de.tudresden.inf.st.sample.communication; + +import de.tudresden.inf.st.pnml.engine.ros.DiNeRosNodeUtil; +import de.tudresden.inf.st.pnml.engine.ros.DiNeRosSubscriber; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonCallbackStorage; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonMarking; +import de.tudresden.inf.st.pnml.jastadd.model.PetriNet; + +public abstract class AbstractDinerosSubscriber implements DiNeRosSubscriber { + + public final boolean execute(BalloonMarking balloonMarking, BalloonCallbackStorage balloonCallbackStorage, PetriNet petriNet, String subnet) { + + boolean success = false; + + // this mechanism enforces the user to leave a server instance with a clean state (see server def) + while (DiNeRosNodeUtil.hasEnabledTransition(petriNet, subnet, balloonMarking)) { + success = subscribe(balloonMarking, balloonCallbackStorage, petriNet, subnet); + } + + return success; + } + + public abstract boolean subscribe(BalloonMarking balloonMarking, BalloonCallbackStorage balloonCallbackStorage, PetriNet petriNet, String subnet); +} diff --git a/src/main/java/de/tudresden/inf/st/sample/communication/Node2Subscriber1.java b/src/main/java/de/tudresden/inf/st/sample/communication/Node2Subscriber1.java new file mode 100644 index 0000000000000000000000000000000000000000..4a912da4ef7cff26f00f06ab63456aae86e23146 --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/communication/Node2Subscriber1.java @@ -0,0 +1,17 @@ +package de.tudresden.inf.st.sample.communication; + +import de.tudresden.inf.st.pnml.jastadd.model.BalloonCallbackStorage; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonMarking; +import de.tudresden.inf.st.pnml.jastadd.model.PetriNet; + +public class Node2Subscriber1 extends AbstractDinerosSubscriber { + + + @Override + public boolean subscribe(BalloonMarking balloonMarking, BalloonCallbackStorage balloonCallbackStorage, PetriNet petriNet, String subnet) { + + // TODO: Implementation by user + + return false; + } +} diff --git a/src/main/java/de/tudresden/inf/st/sample/nodes/Node1.java b/src/main/java/de/tudresden/inf/st/sample/nodes/Node1.java new file mode 100644 index 0000000000000000000000000000000000000000..983ec8f4ea52b53441f21971b15317ff3187a9e2 --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/nodes/Node1.java @@ -0,0 +1,35 @@ +package de.tudresden.inf.st.sample.nodes; + +import de.tudresden.inf.st.pnml.engine.ros.DiNeRosNode; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonCallbackStorage; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonMarking; +import de.tudresden.inf.st.pnml.jastadd.model.PetriNet; +import org.ros.concurrent.CancellableLoop; + +/** + * <Generated> + */ +public class Node1 extends DiNeRosNode { + + public Node1(String nodeName, PetriNet petriNet) { + super(nodeName, petriNet); + } + + public Node1(String nodeName, PetriNet petriNet, BalloonMarking marking, BalloonCallbackStorage callbackStorage) { + super(nodeName, petriNet, marking, callbackStorage); + } + + @Override + protected void nodeLoop() { + + this.connectedNode.executeCancellableLoop(new CancellableLoop() { + + @Override + protected void loop() throws InterruptedException { + + // TODO: Implementation by user + + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/de/tudresden/inf/st/sample/nodes/Node2.java b/src/main/java/de/tudresden/inf/st/sample/nodes/Node2.java new file mode 100644 index 0000000000000000000000000000000000000000..cd647e5ab38f29b280264711be68ef92d6342c68 --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/sample/nodes/Node2.java @@ -0,0 +1,20 @@ +package de.tudresden.inf.st.sample.nodes; + +import de.tudresden.inf.st.pnml.engine.ros.DiNeRosNode; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonCallbackStorage; +import de.tudresden.inf.st.pnml.jastadd.model.BalloonMarking; +import de.tudresden.inf.st.pnml.jastadd.model.PetriNet; + +/** + * <Generated> + */ +public class Node2 extends DiNeRosNode { + + public Node2(String nodeName, PetriNet petriNet) { + super(nodeName, petriNet); + } + + public Node2(String nodeName, PetriNet petriNet, BalloonMarking marking, BalloonCallbackStorage callbackStorage) { + super(nodeName, petriNet, marking, callbackStorage); + } +} diff --git a/src/main/resources/nets/publisher-net.pnml b/src/main/resources/nets/publisher-net.pnml new file mode 100644 index 0000000000000000000000000000000000000000..2d551b03eeecab649c31619fd8ac18f9e09644b9 --- /dev/null +++ b/src/main/resources/nets/publisher-net.pnml @@ -0,0 +1,55 @@ +<pnml xmlns="http://www.pnml.org/version-2009/grammar/pnml"> + <net id="n-E2D0-BCF46-0" type="http://www.pnml.org/version-2009/grammar/ptnet"> + <page id="top"> + <page id="channelPage"> + <place id="channelInPlace"> + <name> + <text>channelInPlace</text> + </name> + <toolspecific tool="de.tudresden.inf.st.pnml.distributedPN" version="0.1"> + + + <location>loc2</location> + <subnet>locA</subnet> + </toolspecific> + <initialMarking> + <text>0</text> + </initialMarking> + </place> + <transition id="channelTransition-publisher-0"> + <name> + <text>channelTransition-publisher-0</text> + </name> + <toolspecific tool="de.tudresden.inf.st.pnml.distributedPN" version="0.0.1"> + + <location>loc2</location> + <type>limitedChannelOutType</type> + <subnet>locA</subnet> + <topic>sampleTopic</topic> + <inputlimit>10</inputlimit> + <outputlimit>10</outputlimit> + <inputsignalbindings/> + </toolspecific> + </transition> + <arc id="a1" source="channelInPlace" target="channelTransition-publisher-0"> + </arc> + </page> + <page id="sourcePage"> + <referencePlace id="rp1" ref="channelInPlace"> + <name> + <text>rp1</text> + </name> + <toolspecific tool="de.tudresden.inf.st.pnml.distributedPN" version="0.1"> + + + <location>loc2</location> + <subnet>locA</subnet> + </toolspecific> + </referencePlace> + </page> + </page> + <name> + <text>topicTest1</text> + </name> + </net> +</pnml> diff --git a/src/main/resources/nets/subscriber-net.pnml b/src/main/resources/nets/subscriber-net.pnml new file mode 100644 index 0000000000000000000000000000000000000000..129a2ca56e4aa872397b15284ba694324d8c581f --- /dev/null +++ b/src/main/resources/nets/subscriber-net.pnml @@ -0,0 +1,55 @@ +<pnml xmlns="http://www.pnml.org/version-2009/grammar/pnml"> + <net id="n-E2D0-BCF46-0" type="http://www.pnml.org/version-2009/grammar/ptnet"> + <page id="top"> + <page id="targetPage"> + <place id="outRefPlace"> + <name> + <text>outRefPlace</text> + </name> + <toolspecific tool="de.tudresden.inf.st.pnml.distributedPN" version="0.1"> + + + <location>loc3</location> + <subnet>locB</subnet> + </toolspecific> + <initialMarking> + <text>0</text> + </initialMarking> + </place> + </page> + <page id="channelPage"> + <transition id="channelTransition-subscriber-0"> + <name> + <text>channelTransition-subscriber-0</text> + </name> + <toolspecific tool="de.tudresden.inf.st.pnml.distributedPN" version="0.0.1"> + + <location>loc3</location> + <type>limitedChannelInType</type> + <subnet>locB</subnet> + <topic>sampleTopic</topic> + <inputlimit>10</inputlimit> + <outputlimit>10</outputlimit> + <inputsignalbindings/> + </toolspecific> + </transition> + <referencePlace id="rp2" ref="outRefPlace"> + <name> + <text>rp2</text> + </name> + <toolspecific tool="de.tudresden.inf.st.pnml.distributedPN" version="0.1"> + + + <location>loc3</location> + <subnet>locB</subnet> + </toolspecific> + </referencePlace> + <arc id="a2" source="channelTransition-subscriber-0" target="rp2"> + </arc> + </page> + </page> + <name> + <text>topicTest1</text> + </name> + </net> +</pnml>