From 153ac831467bf9278d08d960b62909d189acbe64 Mon Sep 17 00:00:00 2001 From: brice-morin Date: Tue, 4 Sep 2018 15:03:25 +0200 Subject: [PATCH] fix inlined split so as not to rely on TelluIoT/ThingML#242 (inspired by @jakhog) --- .../no/sintef/thingml/diversifier/CLI.java | 5 +- .../diversifier/strategies/Helper.java | 4 + .../strategies/SplitMessagesInline.java | 152 +++++++++------- src/main/resources/experiments/java.thingml | 165 ++++++++++++++++++ 4 files changed, 257 insertions(+), 69 deletions(-) create mode 100644 src/main/resources/experiments/java.thingml diff --git a/src/main/java/no/sintef/thingml/diversifier/CLI.java b/src/main/java/no/sintef/thingml/diversifier/CLI.java index c10ae0c..9d5fc59 100644 --- a/src/main/java/no/sintef/thingml/diversifier/CLI.java +++ b/src/main/java/no/sintef/thingml/diversifier/CLI.java @@ -18,7 +18,6 @@ import no.sintef.thingml.diversifier.strategies.DuplicateMessages; import no.sintef.thingml.diversifier.strategies.ShuffleMessages; import no.sintef.thingml.diversifier.strategies.ShuffleParameters; -import no.sintef.thingml.diversifier.strategies.SplitMessages; import no.sintef.thingml.diversifier.strategies.SplitMessagesInline; import no.sintef.thingml.diversifier.strategies.UpsizeParameters; @@ -79,8 +78,8 @@ public static void main(String[] args) throws Exception { } else if (s.equals(Strategies.SHUFF_PARAM.name)) { manager.add(new ShuffleParameters()); } else if (s.equals(Strategies.SPLIT_MSG.name)) { - manager.add(new SplitMessages()); - //manager.add(new SplitMessagesInline()); + //manager.add(new SplitMessages()); + manager.add(new SplitMessagesInline()); } else if (s.equals(Strategies.UP_PARAM.name)) { manager.add(new UpsizeParameters()); } else if (s.equals(Strategies.CODE_MSG.name)) { diff --git a/src/main/java/no/sintef/thingml/diversifier/strategies/Helper.java b/src/main/java/no/sintef/thingml/diversifier/strategies/Helper.java index 9e41965..fc9fa3b 100644 --- a/src/main/java/no/sintef/thingml/diversifier/strategies/Helper.java +++ b/src/main/java/no/sintef/thingml/diversifier/strategies/Helper.java @@ -20,8 +20,12 @@ public static InternalPort createOrGetInternalPort(Thing thing) { return (InternalPort)p; } } + /*final PlatformAnnotation a = ThingMLFactory.eINSTANCE.createPlatformAnnotation(); + a.setName("sync_send"); + a.setValue("true");*/ final InternalPort result = ThingMLFactory.eINSTANCE.createInternalPort(); result.setName("diversified"); + //result.getAnnotations().add(a); thing.getPorts().add(result); return result; } diff --git a/src/main/java/no/sintef/thingml/diversifier/strategies/SplitMessagesInline.java b/src/main/java/no/sintef/thingml/diversifier/strategies/SplitMessagesInline.java index 17c0db2..b4ca5dc 100644 --- a/src/main/java/no/sintef/thingml/diversifier/strategies/SplitMessagesInline.java +++ b/src/main/java/no/sintef/thingml/diversifier/strategies/SplitMessagesInline.java @@ -21,11 +21,11 @@ import org.thingml.xtext.thingML.ConditionalAction; import org.thingml.xtext.thingML.EventReference; import org.thingml.xtext.thingML.Expression; +import org.thingml.xtext.thingML.ExpressionGroup; import org.thingml.xtext.thingML.Function; import org.thingml.xtext.thingML.FunctionCallExpression; import org.thingml.xtext.thingML.Handler; import org.thingml.xtext.thingML.IntegerLiteral; -import org.thingml.xtext.thingML.InternalPort; import org.thingml.xtext.thingML.InternalTransition; import org.thingml.xtext.thingML.LowerExpression; import org.thingml.xtext.thingML.Message; @@ -42,18 +42,13 @@ import org.thingml.xtext.thingML.Thing; import org.thingml.xtext.thingML.ThingMLFactory; import org.thingml.xtext.thingML.ThingMLModel; +import org.thingml.xtext.thingML.ThingMLPackage; import org.thingml.xtext.thingML.TypeRef; import org.thingml.xtext.thingML.VariableAssignment; import no.sintef.thingml.diversifier.Manager; import no.sintef.thingml.diversifier.Mode; -/** - * - * Will not work as long as a critical issue (#242) is not fixed - * in ThingML. See https://github.com/TelluIoT/ThingML/issues/242 - * - */ public class SplitMessagesInline extends Strategy { final Map> duplicates = new HashMap<>(); @@ -97,8 +92,7 @@ protected void doApply(ThingMLModel model) { final List ports = new ArrayList<>(); ports.addAll(t.getPorts()); for (Port port : ports) { - final InternalPort ip = Helper.createOrGetInternalPort(t); - if (EcoreUtil.equals(port, ip)) continue; + if (!Manager.diversify(port)) continue; final List sent = new ArrayList<>(); sent.addAll(port.getSends()); @@ -127,10 +121,8 @@ protected void doApply(ThingMLModel model) { port.getReceives().remove(m); port.getReceives().add(first); port.getReceives().add(second); - ip.getReceives().add(m); - ip.getSends().add(m); - updateHandlers(t, port, m, ip); + updateHandlers(t, port, m); } } } @@ -235,10 +227,12 @@ private void splitSendAction(Thing thing, Port p, Message m, Message first, Mess } } - private void updateHandlers(Thing thing, Port p, Message m, InternalPort ip) { - Set log = new HashSet<>(); - - TreeIterator it = thing.eAllContents(); + private void updateHandlers(Thing thing, Port p, Message m) { + final Set log = new HashSet<>(); + Map props = new HashMap<>(); + Property prop1 = null, prop2 = null; + + final TreeIterator it = thing.eAllContents(); while (it.hasNext()) { final EObject o = it.next(); if (o instanceof Handler) { @@ -249,29 +243,27 @@ private void updateHandlers(Thing thing, Port p, Message m, InternalPort ip) { final State source = (State)t.eContainer(); final Message m1 = duplicates.get(m).get(0); final Message m2 = duplicates.get(m).get(1); - - rm.setPort(ip); - + if (!log.contains(source.getName() + "_" + p.getName() + "_" + m.getName())) { PrimitiveType bool = Helper.getPrimitiveType(Types.BOOLEAN_TYPE, thing); - final Property prop1 = ThingMLFactory.eINSTANCE.createProperty(); + prop1 = ThingMLFactory.eINSTANCE.createProperty(); prop1.setReadonly(false); prop1.setName("received_" + p.getName() + "_" + m1.getName()); final TypeRef tr1 = ThingMLFactory.eINSTANCE.createTypeRef(); tr1.setType(bool); prop1.setTypeRef(tr1); source.getProperties().add(prop1); + props.put("received_" + p.getName() + "_" + m1.getName(), prop1); - final Property prop2 = ThingMLFactory.eINSTANCE.createProperty(); + prop2 = ThingMLFactory.eINSTANCE.createProperty(); prop2.setReadonly(false); prop2.setName("received_" + p.getName() + "_" + m2.getName()); final TypeRef tr2 = ThingMLFactory.eINSTANCE.createTypeRef(); tr2.setType(bool); prop2.setTypeRef(tr2); source.getProperties().add(prop2); + props.put("received_" + p.getName() + "_" + m2.getName(), prop2); - - Map props = new HashMap<>(); for (Parameter param : m.getParameters()) { final Property prop = ThingMLFactory.eINSTANCE.createProperty(); prop.setReadonly(false); @@ -280,59 +272,87 @@ private void updateHandlers(Thing thing, Port p, Message m, InternalPort ip) { source.getProperties().add(prop); props.put(p.getName() + "_" + m.getName() + "_" + param.getName(), prop); } - - final InternalTransition i = ThingMLFactory.eINSTANCE.createInternalTransition(); - source.getInternal().add(i); - final PropertyReference pr1 = ThingMLFactory.eINSTANCE.createPropertyReference(); - pr1.setProperty(prop1); - final PropertyReference pr2 = ThingMLFactory.eINSTANCE.createPropertyReference(); - pr2.setProperty(prop2); - final AndExpression and = ThingMLFactory.eINSTANCE.createAndExpression(); - and.setLhs(pr1); - and.setRhs(pr2); - i.setGuard(and); - final ActionBlock b = ThingMLFactory.eINSTANCE.createActionBlock(); - i.setAction(b); - final BooleanLiteral bool_false = ThingMLFactory.eINSTANCE.createBooleanLiteral(); - bool_false.setBoolValue(false); - final VariableAssignment va1 = ThingMLFactory.eINSTANCE.createVariableAssignment(); - va1.setProperty(prop1); - va1.setExpression(bool_false); - b.getActions().add(va1); - final VariableAssignment va2 = ThingMLFactory.eINSTANCE.createVariableAssignment(); - va2.setProperty(prop2); - va2.setExpression(EcoreUtil.copy(bool_false)); - b.getActions().add(va2); - final SendAction sa = ThingMLFactory.eINSTANCE.createSendAction(); - sa.setPort(ip); - sa.setMessage(m); - for (Parameter param : m.getParameters()) { - final PropertyReference prop = ThingMLFactory.eINSTANCE.createPropertyReference(); - prop.setProperty(props.get(p.getName() + "_" + m.getName() + "_" + param.getName())); - sa.getParameters().add(prop); - } - b.getActions().add(sa); - - //TODO: generate something like this (below) to aggregate both fragments and send original message on internal port - /*internal - guard received_app_m3a and received_app_m3_ - action do - received_app_m3a = false - received_app_m3_ = false - diversified!m3(app_m3_a) - end*/ - + buildTransition(m1, p, source, m); buildTransition(m2, p, source, m); log.add(source.getName() + "_" + p.getName() + "_" + m.getName()); - } + } + + prop1 = props.get("received_" + p.getName() + "_" + m1.getName()); + prop2 = props.get("received_" + p.getName() + "_" + m2.getName()); + + final PropertyReference pr1 = ThingMLFactory.eINSTANCE.createPropertyReference(); + pr1.setProperty(prop1); + final PropertyReference pr2 = ThingMLFactory.eINSTANCE.createPropertyReference(); + pr2.setProperty(prop2); + final AndExpression and = ThingMLFactory.eINSTANCE.createAndExpression(); + and.setLhs(pr1); + and.setRhs(pr2); + + if (t.getGuard() != null) { + final AndExpression andGuard = ThingMLFactory.eINSTANCE.createAndExpression(); + final Expression guard = replaceEventRefByPropRef(t.getGuard(), props); + final ExpressionGroup group = ThingMLFactory.eINSTANCE.createExpressionGroup(); + group.setTerm(guard); + andGuard.setLhs(and); + andGuard.setRhs(group); + t.setGuard(andGuard); + } else { + t.setGuard(and); + } + + ActionBlock b = null; + if (t.getAction() == null) { + b = ThingMLFactory.eINSTANCE.createActionBlock(); + } else if (t.getAction() instanceof ActionBlock) { + b = (ActionBlock)replaceEventRefByPropRef(t.getAction(), props); + } else { + b = ThingMLFactory.eINSTANCE.createActionBlock(); + b.getActions().add(replaceEventRefByPropRef(t.getAction(), props)); + } + + final BooleanLiteral bool_false = ThingMLFactory.eINSTANCE.createBooleanLiteral(); + bool_false.setBoolValue(false); + final VariableAssignment va1 = ThingMLFactory.eINSTANCE.createVariableAssignment(); + va1.setProperty(prop1); + va1.setExpression(bool_false); + b.getActions().add(va1); + final VariableAssignment va2 = ThingMLFactory.eINSTANCE.createVariableAssignment(); + va2.setProperty(prop2); + va2.setExpression(EcoreUtil.copy(bool_false)); + b.getActions().add(va2); + + t.eUnset(ThingMLPackage.eINSTANCE.getHandler_Event()); } } } } } + private T replaceEventRefByPropRef(T e, Map props) { + final TreeIterator it = e.eAllContents(); + while (it.hasNext()) { + final EObject o = it.next(); + if (o instanceof EventReference) { + final EventReference er = (EventReference) o; + final PropertyReference pr = ThingMLFactory.eINSTANCE.createPropertyReference(); + final ReceiveMessage rm = (ReceiveMessage)er.getReceiveMsg(); + final Property p = props.get(rm.getPort().getName() + "_" + rm.getMessage().getName() + "_" + er.getParameter().getName()); + if (p == null) continue; + pr.setProperty(p); + final Object ref = er.eContainer().eGet(er.eContainingFeature()); + if (ref instanceof EList) { + ((EList)ref).add(((EList)ref).indexOf(er), pr); + ((EList)ref).remove(er); + } else { + er.eContainer().eSet(er.eContainingFeature(), pr); + } + } + } + return e; + } + private void buildTransition(Message m, Port p, State from, Message orig) { System.out.println("Building new handler in " + from.getName() + " on event " + p.getName() + "?" + m.getName()); final ReceiveMessage rm = ThingMLFactory.eINSTANCE.createReceiveMessage(); diff --git a/src/main/resources/experiments/java.thingml b/src/main/resources/experiments/java.thingml new file mode 100644 index 0000000..acd6933 --- /dev/null +++ b/src/main/resources/experiments/java.thingml @@ -0,0 +1,165 @@ +import "datatypes.thingml" from stl + +protocol Default; + +thing fragment Msgs { + message m1(a : Byte@upsize "not", b : Byte@upsize "not", c : Integer, d : Byte@weakparam "true", e : Byte)@code "0" + message m2(a : Byte@upsize "not", b : Byte@upsize "not", c : Byte)@code "1" + message m3(a : Byte)@code "2" + + function rnd() : Byte do + return `Math.floor(Math.random() * Math.floor(256))` + end + + property counter : Integer = 0 + property N : Integer = 250 + + property bytesSentCounter : Long = 0 +} + +thing App includes Msgs +@stdout_sync "true" +{ + + property id : Byte + + provided port app { + receives m1, m2 + sends m3 + } + + statechart init WaitForM1 { + on entry `//TODO: write sysout to log file` + + state WaitForM1 { + transition -> WaitForM2 + event e : app?m1 + action do + id = e.a + println "#APP: Ooh, I needed that app?m1(", e.a, ", ", e.b, ", ", e.c, ", ", e.d, ", ", e.e, ")!" + end + } + + state WaitForM2 { + transition -> SendAck + event e : app?m2 + action println "#APP: Ooh, I needed that app?m2(", e.a, ", ", e.b, ", ", e.c, ")!" + } + + state SendAck { + on entry do + println "#APP: Come get some app!m3(", id, ")!" + app!m3(id) + counter++ + end + + transition -> WaitForM1 guard counter < N + transition -> DONE guard counter >= N + } + + final state DONE { + on entry do + println "#APP: sent ", bytesSentCounter, " bts." + end + } + } + +} + +thing Client includes Msgs +@stdout_sync "true" +{ + + property start : Integer + + property _a : Byte + property _b : Byte + property _c : Byte + property _d : Byte + property _e : Byte + + required port app { + sends m1, m2 + receives m3 + } + + statechart init RUN { + on entry do + _a = rnd() + _b = 0x02 //rnd() + start = `System.currentTimeMillis()` + end + + state RUN { + on entry do + _c = rnd() % 20 + _d = rnd() % 10 + 20 + _e = rnd() % 5 + 10 + println "#CLI: Come get some app!m1(", _a, ", ", _b, ", ", _c, ", ", _d, ", ", _e, ")!" + app!m1(_a, _b, _c, _d, _e) + println "#CLI: Come get some app!m2(", _a, ", ", _b, ", ", _c, ")!" + app!m2(_a, _b, _c) + counter++ + end + + transition -> STOP + event e : app?m3 + guard counter >= N + + transition -> RUN + event e : app?m3 + guard e.a == _a and counter < N + action do + println "#CLI: Ooh, I needed that app?m3(", e.a, ")!" + println "#CLI: We meet again, Doctor Jones!" + end + + transition -> ERROR + event e : app?m3 + guard e.a != _a + action do + println "#CLI: Ooh, I needed that app?m3(", e.a, ")!" + println "#CLI: Damn, you re ugly." + end + } + + final state STOP { + on entry do + println "#CLI: What are you waitin for? Christmas?" + var duration : Integer = `System.currentTimeMillis()` - start + println "#CLI: took ", duration, "ms." + /* + ` + const memwatch = require('memwatch-next'); + const hd = new memwatch.HeapDiff(); + const diff = hd.end(); + ` + */ + //println "#CLI: used ", `diff.after.size_bytes`, " bts." + println "#CLI: sent ", bytesSentCounter, " bts." + /*`process.writeable.on('close', () => { + process.exit(0); + }); + process.writeable.end();`*/ + end + } + + final state ERROR { + on entry do + println "#CLI: Heh, heh, heh... what a mess!" + /*`process.writeable.on('close', () => { + process.exit(1); + }); + process.writeable.end();`*/ + end + } + } + +} + +configuration test { + instance app : App + instance client1 : Client + + connector client1.app => app.app +}