Browse Source

Merge tag 'python_urllib' into testing

Defines the latest version of the interface which uses the Python urllib libraries for HTTP communication
Yentl Van Tendeloo 8 years ago
parent
commit
52a4bfb1d3
7 changed files with 412 additions and 53 deletions
  1. 105 0
      unit/log_output.py
  2. 40 0
      unit/log_output.xml
  3. 15 2
      unit/test_all.py
  4. 78 40
      wrappers/modelverse.py
  5. 115 0
      wrappers/poll_print.py
  6. 43 0
      wrappers/poll_print.xml
  7. 16 11
      wrappers/test_SCCD.py

+ 105 - 0
unit/log_output.py

@@ -0,0 +1,105 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Date:   Wed Aug 23 14:57:48 2017
+
+Model author: Yentl Van Tendeloo
+Model name:   Logging
+Model description:
+For testing: append all input to a log until finished
+"""
+
+from sccd.runtime.statecharts_core import *
+
+# package "Logging"
+
+class Logging(RuntimeClassBase):
+    def __init__(self, controller, log):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        Logging.user_defined_constructor(self, log)
+    
+    def user_defined_constructor(self, log):
+        self.log = log
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /init
+        self.states["/init"] = State(1, "/init", self)
+        self.states["/init"].setEnter(self._init_enter)
+        self.states["/init"].setExit(self._init_exit)
+        
+        # state /finished
+        self.states["/finished"] = State(2, "/finished", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/init"])
+        self.states[""].addChild(self.states["/finished"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/init"]
+        
+        # transition /init
+        _init_0 = Transition(self, self.states["/init"], [self.states["/init"]])
+        _init_0.setAction(self._init_0_exec)
+        _init_0.setTrigger(Event("input", "inp"))
+        self.states["/init"].addTransition(_init_0)
+        _init_1 = Transition(self, self.states["/init"], [self.states["/init"]])
+        _init_1.setTrigger(Event("_0after"))
+        self.states["/init"].addTransition(_init_1)
+        _init_2 = Transition(self, self.states["/init"], [self.states["/finished"]])
+        _init_2.setTrigger(Event("terminate", "inp"))
+        self.states["/init"].addTransition(_init_2)
+    
+    def _init_enter(self):
+        self.addTimer(0, 10)
+    
+    def _init_exit(self):
+        self.removeTimer(0)
+    
+    def _init_0_exec(self, parameters):
+        value = parameters[0]
+        self.log.append(value)
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/init"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class ObjectManager(ObjectManagerBase):
+    def __init__(self, controller):
+        ObjectManagerBase.__init__(self, controller)
+    
+    def instantiate(self, class_name, construct_params):
+        if class_name == "Logging":
+            instance = Logging(self.controller, construct_params[0])
+            instance.associations = {}
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(ThreadsControllerBase):
+    def __init__(self, log, keep_running = None, behind_schedule_callback = None):
+        if keep_running == None: keep_running = True
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.addInputPort("inp")
+        self.addOutputPort("outp")
+        self.object_manager.createInstance("Logging", [log])

+ 40 - 0
unit/log_output.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<diagram author="Yentl Van Tendeloo" name="Logging">
+    <description>
+        For testing: append all input to a log until finished
+    </description>
+
+    <inport name="inp"/>
+    <outport name="outp"/>
+
+    <class name="Logging" default="true">
+        <constructor>
+            <parameter name="log"/>
+            <body>
+                self.log = log
+                print("SC initialized")
+            </body>
+        </constructor>
+        <scxml initial="init">
+            <state id="init">
+                <transition event="input" port="inp" target=".">
+                    <parameter name="value"/>
+                    <script>
+                        self.log.append(value)
+                        print("Got value: " + str(value))
+                    </script>
+                </transition>
+
+                <transition after="10" target="."/>
+
+                <transition event="terminate" port="inp" target="../finished"/>
+            </state>
+
+            <state id="finished">
+                <script>
+                    print("FINISHED")
+                </script>
+            </state>
+        </scxml>
+    </class>
+</diagram>

+ 15 - 2
unit/test_all.py

@@ -209,8 +209,15 @@ class TestModelverse(unittest.TestCase):
         transformation_add_AL({"PetriNet_Runtime": "test/PetriNet_Runtime"}, {"PetriNet_Runtime": "test/PetriNet_Runtime"}, "test/pn_simulate", open("integration/code/pn_simulate.alc").read())
         transformation_add_MT({"PetriNet_Runtime": "test/PetriNet_Runtime"}, {"PetriNet": "test/PetriNet"}, "test/pn_runtime_to_design", open("integration/code/pn_runtime_to_design.mvc").read(), add_tracability_R2D)
 
+        import log_output
         log = []
-        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, callback) == True
+        ctrl = log_output.Controller(log, keep_running=False)
+        thrd = threading.Thread(target=ctrl.start)
+        thrd.daemon = True
+        thrd.start()
+
+        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == None
+        thrd.join()
         assert set(log) == set(['"p1" --> 1',
                                 '"p2" --> 2',
                                 '"p3" --> 3'])
@@ -220,7 +227,13 @@ class TestModelverse(unittest.TestCase):
         assert transformation_execute_MT("test/pn_runtime_to_design", {"PetriNet_Runtime": "test/my_pn_RT"}, {"PetriNet": "test/my_pn"}) == True
 
         log = []
-        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, callback) == True
+        ctrl = log_output.Controller(log, keep_running=False)
+        thrd = threading.Thread(target=ctrl.start)
+        thrd.daemon = True
+        thrd.start()
+
+        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == None
+        thrd.join()
         assert set(log) == set(['"p1" --> 0',
                                 '"p2" --> 1',
                                 '"p3" --> 5'])

+ 78 - 40
wrappers/modelverse.py

@@ -8,6 +8,8 @@ import time
 import threading
 import Queue
 
+from sccd.runtime.statecharts_core import Event
+
 COMPILER_PATH = "interface/HUTN"
 
 MODE_UNCONNECTED = 0
@@ -62,14 +64,13 @@ class UnknownMetamodellingHierarchy(ModelverseException):
 # Helper functions and configuration: do not use yourself!
 taskname = None
 address = None
-last_output = None
 mode = MODE_UNCONNECTED
 prev_mode = None
 current_model = None
 registered_metamodels = {}
-outputs = []
+outputs = [None]
 
-def _output_thread(outputs):
+def _output_thread(outputs, taskname):
     while taskname is None:
         time.sleep(0.1)
 
@@ -79,9 +80,36 @@ def _output_thread(outputs):
     except:
         pass
 
-thrd = threading.Thread(target=_output_thread, args=[outputs])
-thrd.daemon = True
-thrd.start()
+def _exec_on_statechart(statechart):
+    def _exec_sc(controller, inport, outport):
+        op = controller.addOutputListener(outport)
+
+        while 1:
+            if len(outputs) > 1:
+                # Is an output message of the Mv, so put it in the SC
+                del outputs[0]
+                output_event = outputs[0]
+
+                if output_event == "Success" or output_event == "Failure":
+                    # Is a stop event!
+                    controller.addInput(Event("terminate", inport, []))
+                    break
+                else:
+                    # Is just a normal event!
+                    controller.addInput(Event("input", inport, [output_event]))
+
+            input_event = op.fetch(0)
+            if input_event is not None:
+                # Expand the event and make it HTTP input
+                _input(input_event.parameters)
+                
+            time.sleep(0.01)
+        
+    thrd = threading.Thread(target=_exec_sc, args=statechart)
+    thrd.daemon = True
+    thrd.start()
+
+    return None
 
 def _get_metamodel(model):
     global registered_metamodels
@@ -132,7 +160,7 @@ def _goto_mode(new_mode, model_name=None):
 
 def _input(value, port=None):
     # Ugly json encoding of primitives
-    #print("[IN] %s" % value)
+    print("[IN] %s" % value)
     if port is None:
         port = taskname
     if isinstance(value, type([])):
@@ -147,8 +175,6 @@ def _input_raw(value, taskname):
     # Ugly json encoding of primitives
     urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
 
-    #TODO check that this is actually a Modelverse!
-
 def _compile_AL(code):
     # Compile an action language file and send the compiled code
     code_fragments = code.split("\n")
@@ -185,22 +211,21 @@ def _compile_model(code):
 
 def _output(expected=None):
     try:
-        global last_output
+        while len(outputs) < 2:
+            time.sleep(0.5)
+            print("Sleep for output: " + str(outputs))
 
-        while not outputs:
-            time.sleep(0.01)
-
-        last_output = outputs.pop(0)
-        #print("[OUT] %s" % last_output)
+        del outputs[0]
+        print("[OUT] %s" % outputs[0])
     except:
         raise UnknownError()
 
-    if expected is not None and last_output != expected:
+    if expected is not None and _last_output() != expected:
         raise InterfaceMismatch(_last_output(), expected)
-    return last_output
+    return _last_output()
 
 def _last_output():
-    return last_output
+    return outputs[0]
 
 # Raise common exceptions
 def _handle_output(requested=None, split=None):
@@ -288,9 +313,18 @@ def init(address_param="http://127.0.0.1:8001", timeout=20.0):
                 raise ConnectionError(e.reason)
             else:
                 time.sleep(0.1)
+
+    global outputs
     global taskname
+
+    outputs = [None]
     taskname = task
 
+    # This re-assign also diconnects the previous get_output connections to the outputs variable
+    thrd = threading.Thread(target=_output_thread, args=[outputs, task])
+    thrd.daemon = True
+    thrd.start()
+
 def login(username, password):
     """Log in a user, if user doesn't exist, it is created."""
     global mode
@@ -521,7 +555,7 @@ def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_na
 
     _handle_output("Success")
 
-def transformation_execute_AL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
+def transformation_execute_AL(operation_name, input_models_dict, output_models_dict, statechart=None):
     """Execute an existing model operation."""
     global mode
     _goto_mode(MODE_MODELLING)
@@ -531,19 +565,21 @@ def transformation_execute_AL(operation_name, input_models_dict, output_models_d
     _input(["transformation_execute", operation_name] + mv_dict_rep)
     _handle_output("Success: ready for AL execution")
 
-    # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
-    while _output() not in ["Success", "Failure"]:
+    if statechart is not None:
+        # We are to delegate this information to another statechart, which wants interaction
+        # As such, we continue on a different thread, where we pipe the information, and let this call return immediately
         mode = MODE_DIALOG
-        reply = callback(_last_output())
+        _exec_on_statechart(statechart)
         mode = MODE_MODELLING
-        if reply is not None:
-            _input(reply)
-
-    # Got termination message, so we are done!
-    if _last_output() == "Success":
-        return True
+        return None
     else:
-        return False
+        # No statechart associated, so just wait until we are finished
+        while _output() not in ["Success", "Failure"]:
+            pass
+        if _last_output() == "Success":
+            return True
+        else:
+            return False
 
 def transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
     """Execute an existing model operation."""
@@ -572,7 +608,7 @@ def transformation_execute_MANUAL(operation_name, input_models_dict, output_mode
     else:
         return False
 
-def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
+def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, statechart=None):
     """Execute an existing model operation."""
     global mode
     _goto_mode(MODE_MODELLING)
@@ -582,19 +618,21 @@ def transformation_execute_MT(operation_name, input_models_dict, output_models_d
     _input(["transformation_execute", operation_name] + mv_dict_rep)
     _handle_output("Success: ready for MT execution")
 
-    # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
-    while _output() not in ["Success", "Failure"]:
+    if statechart is not None:
+        # We are to delegate this information to another statechart, which wants interaction
+        # As such, we continue on a different thread, where we pipe the information, and let this call return immediately
         mode = MODE_DIALOG
-        reply = callback(_last_output())
+        _exec_on_statechart(statechart)
         mode = MODE_MODELLING
-        if reply is not None:
-            _input(reply)
-
-    # Got termination message, so we are done!
-    if _last_output() == "Success":
-        return True
+        return None
     else:
-        return False
+        # No statechart associated, so just wait until we are finished
+        while _output() not in ["Success", "Failure"]:
+            pass
+        if _last_output() == "Success":
+            return True
+        else:
+            return False
 
 def transformation_list():
     """List existing model operations."""

+ 115 - 0
wrappers/poll_print.py

@@ -0,0 +1,115 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Date:   Wed Aug 23 16:04:59 2017
+
+Model author: Yentl Van Tendeloo
+Model name:   Logging
+Model description:
+For testing: I/O Statechart for the Modelverse
+"""
+
+from sccd.runtime.statecharts_core import *
+
+# package "Logging"
+
+class Logging(RuntimeClassBase):
+    def __init__(self, controller):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        Logging.user_defined_constructor(self)
+    
+    def user_defined_constructor(self):
+        pass
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /init
+        self.states["/init"] = State(1, "/init", self)
+        self.states["/init"].setEnter(self._init_enter)
+        self.states["/init"].setExit(self._init_exit)
+        
+        # state /finished
+        self.states["/finished"] = State(2, "/finished", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/init"])
+        self.states[""].addChild(self.states["/finished"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/init"]
+        
+        # transition /init
+        _init_0 = Transition(self, self.states["/init"], [self.states["/init"]])
+        _init_0.setAction(self._init_0_exec)
+        _init_0.setTrigger(Event("input", "inp"))
+        self.states["/init"].addTransition(_init_0)
+        _init_1 = Transition(self, self.states["/init"], [self.states["/init"]])
+        _init_1.setTrigger(Event("_0after"))
+        self.states["/init"].addTransition(_init_1)
+        _init_2 = Transition(self, self.states["/init"], [self.states["/finished"]])
+        _init_2.setTrigger(Event("terminate", "inp"))
+        self.states["/init"].addTransition(_init_2)
+        _init_3 = Transition(self, self.states["/init"], [self.states["/init"]])
+        _init_3.setAction(self._init_3_exec)
+        _init_3.setTrigger(Event("raw_inp", "user_inp"))
+        self.states["/init"].addTransition(_init_3)
+    
+    def _init_enter(self):
+        self.addTimer(0, 10)
+    
+    def _init_exit(self):
+        self.removeTimer(0)
+    
+    def _init_0_exec(self, parameters):
+        value = parameters[0]
+        print(value)
+    
+    def _init_3_exec(self, parameters):
+        inp = parameters[0]
+        print("Got raw input")
+        self.big_step.outputEvent(Event("output", "outp", [inp]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/init"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class ObjectManager(ObjectManagerBase):
+    def __init__(self, controller):
+        ObjectManagerBase.__init__(self, controller)
+    
+    def instantiate(self, class_name, construct_params):
+        if class_name == "Logging":
+            instance = Logging(self.controller)
+            instance.associations = {}
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(ThreadsControllerBase):
+    def __init__(self, keep_running = None, behind_schedule_callback = None):
+        if keep_running == None: keep_running = True
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.addInputPort("user_inp")
+        self.addInputPort("inp")
+        self.addOutputPort("outp")
+        self.object_manager.createInstance("Logging", [])

+ 43 - 0
wrappers/poll_print.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<diagram author="Yentl Van Tendeloo" name="Logging">
+    <description>
+        For testing: I/O Statechart for the Modelverse
+    </description>
+
+    <inport name="user_inp"/>
+    <inport name="inp"/>
+    <outport name="outp"/>
+
+    <class name="Logging" default="true">
+        <scxml initial="init">
+            <state id="init">
+                <transition event="input" port="inp" target=".">
+                    <parameter name="value"/>
+                    <script>
+                        print(value)
+                    </script>
+                </transition>
+
+                <transition after="10" target="."/>
+
+                <transition event="terminate" port="inp" target="../finished"/>
+
+                <transition event="raw_inp" port="user_inp" target=".">
+                    <parameter name="inp"/>
+                    <script>
+                        print("Got raw input")
+                    </script>
+                    <raise event="output" port="outp" scope="output">
+                        <parameter expr="inp"/>
+                    </raise>
+                </transition>
+            </state>
+
+            <state id="finished">
+                <script>
+                    print("FINISHED")
+                </script>
+            </state>
+        </scxml>
+    </class>
+</diagram>

+ 16 - 11
wrappers/test_SCCD.py

@@ -1,6 +1,7 @@
 import sys
 sys.path.append("wrappers")
 from modelverse import *
+from sccd.runtime.statecharts_core import Event
 
 init()
 login("admin", "admin")
@@ -19,19 +20,23 @@ except ModelExists:
 # Update the model of the traffic light
 if "my_SCCD" in [i[0] for i in model_list("models")]:
     model_delete("models/my_SCCD")
-#model_add("models/my_SCCD", "formalisms/SCCD", open("models/dynamic_trafficlight.mvc", 'r').read())
-model_add("models/my_SCCD", "formalisms/SCCD", open("integration/code/SCCD_all.mvc", 'r').read())
+model_add("models/my_SCCD", "formalisms/SCCD", open("models/dynamic_trafficlight.mvc", 'r').read())
 
 # Add SCCD execution semantics
 transformation_add_AL({"SCCD": "formalisms/SCCD"}, {"trace": "formalisms/SCCD_Trace"}, "models/SCCD_execute", open("models/SCCD_execute.alc", 'r').read())
 
-# Execute it, while interacting through the console
-def callback(inp):
-    print(inp)
-    inp = raw_input()
-    if inp == "":
-        return None
-    else:
-        return inp
+import poll_print
+ctrl = poll_print.Controller(keep_running=False)
 
-transformation_execute_AL("models/SCCD_execute", {"SCCD": "models/my_SCCD"}, {"trace": "models/my_trace"}, callback=callback)
+thrd = threading.Thread(target=ctrl.start)
+thrd.daemon = True
+thrd.start()
+
+transformation_execute_AL("models/SCCD_execute", {"SCCD": "models/my_SCCD"}, {"trace": "models/my_trace"}, statechart=(ctrl, "inp", "outp"))
+
+import select
+while thrd.is_alive():
+    if select.select([sys.stdin], [], [], 0.1)[0]:
+        ctrl.addInput(Event("raw_inp", "user_inp", [sys.stdin.readline().strip()]))
+
+thrd.join()