Browse Source

Reset to old modelverse_coded, which is 100% coded

Yentl Van Tendeloo 8 years ago
parent
commit
31679cb17c
2 changed files with 249 additions and 271 deletions
  1. 22 22
      integration/test_powerwindow.py
  2. 227 249
      wrappers/modelverse_coded.py

+ 22 - 22
integration/test_powerwindow.py

@@ -35,20 +35,20 @@ class TestPowerWindow(unittest.TestCase):
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Query": "formalisms/Query"}, {"Query": "formalisms/Query"}, "models/revise_query")
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Architecture": "formalisms/Architecture"}, {"Architecture": "formalisms/Architecture"}, "models/revise_architecture")
 
-        def tracability_CTRL2EPN():
-            instantiate(None, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link")
-            instantiate(None, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_tlink")
+        def tracability_CTRL2EPN(context):
+            instantiate(None, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link", context=context)
+            instantiate(None, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_tlink", context=context)
 
-        def tracability_PLANT2EPN():
-            instantiate(None, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link")
-            instantiate(None, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink")
+        def tracability_PLANT2EPN(context):
+            instantiate(None, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link", context=context)
+            instantiate(None, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink", context=context)
 
-        def tracability_ENV2EPN():
-            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link")
+        def tracability_ENV2EPN(context):
+            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link", context=context)
 
-        def tracability_EPN2PN():
-            instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
-            instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
+        def tracability_EPN2PN(context):
+            instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link", context=context)
+            instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link", context=context)
 
         transformation_add_MT({}, {"PW_Plant": "formalisms/PW_Plant", "PW_Environment": "formalisms/PW_Environment", "PW_Control": "formalisms/PW_Control", "Query": "formalisms/Query", "Architecture": "formalisms/Architecture", "Requirements": "formalisms/Requirements"}, "models/make_initial_models", open("models/initialize.mvc", 'r').read())
         transformation_add_MT({"PW_Plant": "formalisms/PW_Plant"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/plant_to_EPN", open("models/plant_to_EPN.mvc", 'r').read(), tracability_PLANT2EPN)
@@ -123,20 +123,20 @@ class TestPowerWindow(unittest.TestCase):
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Query": "formalisms/Query"}, {"Query": "formalisms/Query"}, "models/revise_query")
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Architecture": "formalisms/Architecture"}, {"Architecture": "formalisms/Architecture"}, "models/revise_architecture")
 
-        def tracability_CTRL2EPN():
-            instantiate(None, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link")
-            instantiate(None, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_tlink")
+        def tracability_CTRL2EPN(context):
+            instantiate(None, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link", context=context)
+            instantiate(None, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_tlink", context=context)
 
-        def tracability_PLANT2EPN():
-            instantiate(None, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link")
-            instantiate(None, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink")
+        def tracability_PLANT2EPN(context):
+            instantiate(None, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link", context=context)
+            instantiate(None, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink", context=context)
 
-        def tracability_ENV2EPN():
-            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link")
+        def tracability_ENV2EPN(context):
+            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link", context=context)
 
-        def tracability_EPN2PN():
-            instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
-            instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
+        def tracability_EPN2PN(context):
+            instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link", context=context)
+            instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link", context=context)
 
         transformation_add_MT({}, {"PW_Plant": "formalisms/PW_Plant", "PW_Environment": "formalisms/PW_Environment", "PW_Control": "formalisms/PW_Control", "Query": "formalisms/Query", "Architecture": "formalisms/Architecture", "Requirements": "formalisms/Requirements"}, "models/make_initial_models", open("models/initialize.mvc", 'r').read())
         transformation_add_MT({"PW_Plant": "formalisms/PW_Plant"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/plant_to_EPN", open("models/plant_to_EPN.mvc", 'r').read(), tracability_PLANT2EPN)

+ 227 - 249
wrappers/modelverse_coded.py

@@ -1,13 +1,13 @@
 import urllib
+import urllib2
 import json
 import random
+from urllib2 import URLError
 import sys
 import time
 import threading
-import uuid
 
-import sccd.runtime.socket2event as socket2event
-from sccd.runtime.statecharts_core import Event
+COMPILER_PATH = "interface/HUTN"
 
 MODE_UNCONNECTED = 0
 MODE_UNAUTHORIZED = 1
@@ -17,6 +17,10 @@ MODE_DIALOG = 4
 MODE_MANUAL = 5
 MODE_SERVICE = 6
 
+# Bind to the compiler (might have to update path manually!)
+sys.path.append(COMPILER_PATH)
+from hutn_compiler.compiler import main as do_compile
+
 # Exceptions
 class ModelverseException(Exception):
     pass
@@ -56,60 +60,12 @@ 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 = {}
-ctrl_input = None
-ctrl_output = None
-
-def _output_thread(controller, outp, task):
-    req_out = controller.addOutputListener("request_out")
-    my_id = str(uuid.uuid4())
-
-    try:
-        while 1:
-            controller.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "get_output", "taskname": task}), my_id]))
-            event = req_out.fetch(-1)
-
-            print("Got event in coded: " + str(event))
-            if event.parameters[1] == my_id:
-                outp[task].append(json.loads(event.parameters[0]))
-    except:
-        raise
-
-def _exec_on_statechart(statechart):
-    def _exec_sc(controller, inport, outport):
-        global taskname
-        op = controller.addOutputListener(outport)
-
-        while 1:
-            if len(outputs[taskname]) > 1:
-                # Is an output message of the Mv, so put it in the SC
-                del outputs[taskname][0]
-                output_event = outputs[taskname][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.02)
-        
-    thrd = threading.Thread(target=_exec_sc, args=statechart)
-    thrd.daemon = True
-    thrd.start()
-
-    return None
 
 def _get_metamodel(model):
     global registered_metamodels
@@ -158,47 +114,77 @@ def _goto_mode(new_mode, model_name=None):
         # Go to a mode that we have no automatic transfer to: raise exception
         raise InvalidMode("Required mode: %s, current mode: %s" % (new_mode, mode))
 
-def _input(value, task=None):
+def _input(value, port=None):
     # Ugly json encoding of primitives
     #print("[IN] %s" % value)
-    if task is None:
-        task = taskname
+    if port is None:
+        port = taskname
     if isinstance(value, type([])):
         value = json.dumps(value)
-        ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": task, "data": value}), None]))
+        urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": value, "taskname": port}))).read()
     else:
         value = json.dumps(value)
-        ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": task, "value": value}), None]))
+        #print("Set input: " + str(value))
+        urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": port}))).read()
 
 def _input_raw(value, taskname):
     # Ugly json encoding of primitives
-    ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": taskname, "value": value}), None]))
-
-def _output(expected=None, task=None):
-    if task is None:
-        task = taskname
-
+    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")
+    code_fragments = [i for i in code_fragments if i.strip() != ""]
+    code_fragments = [i.replace("    ", "\t") for i in code_fragments]
+    initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
+    code_fragments = [i[initial_tabs:] for i in code_fragments]
+    code_fragments.append("")
+    code = "\n".join(code_fragments)
+
+    with open(".code.alc", "w") as f:
+        f.write(code)
+        f.flush()
+
+    compiled = do_compile(".code.alc", COMPILER_PATH + "/grammars/actionlanguage.g", "CS")
+    return compiled
+
+def _compile_model(code):
+    # Compile a model and send the compiled graph
+    # First change multiple spaces to a tab
+    code_fragments = code.split("\n")
+    code_fragments = [i for i in code_fragments if i.strip() != ""]
+    code_fragments = [i.replace("    ", "\t") for i in code_fragments]
+    initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
+    code_fragments = [i[initial_tabs:] for i in code_fragments]
+    code_fragments.append("")
+    code = "\n".join(code_fragments)
+
+    with open(".model.mvc", "w") as f:
+        f.write(code)
+        f.flush()
+
+    return do_compile(".model.mvc", COMPILER_PATH + "/grammars/modelling.g", "M")
+
+def _output(expected=None,port=None):
+    if port is None:
+        port = taskname
     try:
-        while len(outputs[task]) < 2:
-            time.sleep(0.02)
-
-        del outputs[task][0]
-        #print("[OUT] %s" % outputs[0])
+        global last_output
+        last_output = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": port}))).read())
+        #print("[OUT] %s" % last_output)
     except:
         raise UnknownError()
+    if expected is not None and last_output != expected:
+        raise InterfaceMismatch(_last_output(), expected)
+    return last_output
 
-    if expected is not None and _last_output(task) != expected:
-        raise InterfaceMismatch(_last_output(task), expected)
-    return _last_output(task)
-
-def _last_output(task=None):
-    if task is None:
-        task = taskname
-
-    return outputs[task][0]
+def _last_output():
+    return last_output
 
 # Raise common exceptions
-def _handle_output(requested=None, split=False):
+def _handle_output(requested=None, split=None):
     value = _output()
     if value.startswith("Model exists: "):
         raise ModelExists(value.split(": ", 1)[1])
@@ -213,14 +199,14 @@ def _handle_output(requested=None, split=False):
     elif value.startswith("Attribute not found: "):
         raise NoSuchAttribute(value.split(": ", 1)[1])
     elif requested is not None and value.startswith(requested):
-        if not split:
+        if split is None:
             return value
         else:
-            splitted = value.strip().split(" ", 1)
-            if len(splitted) > 1:
-                return splitted[1].split("\n")
+            splitted = value.strip().split(split, 1)
+            if len(splitted) == 1:
+                return ""
             else:
-                return []
+                return splitted[1].rstrip()
     else:
         raise InterfaceMismatch(value)
     
@@ -265,75 +251,25 @@ def alter_context(model_name, metamodel_name):
     global registered_metamodels
     registered_metamodels[model_name] = metamodel_name
 
-def _start_http_client(addr, port, timeout):
-    import http_client
-    ctrl = http_client.Controller()
-    listener = ctrl.addOutputListener("request_out")
-    socket2event.boot_translation_service(ctrl)
-    thrd = threading.Thread(target=ctrl.start)
-    thrd.daemon = True
-    thrd.start()
-
-    evt = listener.fetch(-1)
-    if evt.name != "http_client_initialized":
-        raise Exception("HTTP client did not behave as expected during init: " + str(evt.name))
-
-    ctrl.addInput(Event("connect", "request_in", [(addr, port), timeout]))
-
-    evt = listener.fetch(-1)
-
-    if evt.name == "http_client_timeout":
-        raise Exception("HTTP client timeout")
-    if evt.name != "http_client_ready":
-        raise Exception("HTTP client did not behave as expected during connect: " + str(evt.name))
-
-    return ctrl
-
 # Main MvC operations
 def init(address_param="127.0.0.1:8001", timeout=20.0):
     """Starts up the connection to the Modelverse."""
-    # Start up the HTTP Client SC
-    global ctrl_input
-    global ctrl_output
-
-    if ctrl_input is not None:
-        ctrl_input.stop()
-    if ctrl_output is not None:
-        ctrl_output.stop()
-
-    global address
-    global port
-
-    address, port = address_param.split(":", 1)
-    port = int(port)
-    
-    ctrl_input = _start_http_client(address, port, timeout)
-    ctrl_output = _start_http_client(address, port, timeout) 
-    controllers = [ctrl_input, ctrl_output]
-
     global mode
-    start_time = time.time()
-    task = str(random.random())
-
-    _input_raw('"%s"' % task, "task_manager")
-
-    mode = MODE_UNAUTHORIZED
-
-    global outputs
+    global address
     global taskname
-    taskname = task
-    outputs = {}
-
-    _listen_to_output(ctrl_output, task)
-
-def _listen_to_output(controller, task):
-    global outputs
-    outputs[task] = [None]
-
-    # This re-assign also diconnects the previous get_output connections to the outputs variable
-    thrd = threading.Thread(target=_output_thread, args=[controller, outputs, task])
-    thrd.daemon = True
-    thrd.start()
+    address = "http://%s" % address_param
+    start_time = time.time()
+    taskname = random.random()
+    while 1:
+        try:
+            _input_raw('"%s"' % taskname, "task_manager")
+            mode = MODE_UNAUTHORIZED
+            break
+        except URLError as e:
+            if time.time() - start_time > timeout:
+                raise ConnectionError(e.reason)
+            else:
+                time.sleep(0.1)
 
 def login(username, password):
     """Log in a user, if user doesn't exist, it is created."""
@@ -369,20 +305,34 @@ def login(username, password):
     else:
         raise InterfaceMismatch(_last_output())
 
-def model_add(model_name, metamodel_name, model_code=""):
+def model_add(model_name, metamodel_name, model_code=None):
     """Instantiate a new model."""
     _goto_mode(MODE_MODELLING)
 
+    # Do this before creating the model, as otherwise compilation errors would make us inconsistent
+    if model_code is not None:
+        try:
+            compiled = _compile_model(model_code)
+        except Exception as e:
+            raise CompilationError(e)
+    else:
+        compiled = [0]
+
     _input(["model_add", metamodel_name, model_name])
     _handle_output("Waiting for model constructors...")
-    _input(model_code)
+    _input(compiled)
     _output("Success")
 
     global registered_metamodels
     registered_metamodels[model_name] = metamodel_name
 
 def upload_code(code):
-    _input(code)
+    try:
+        compiled = _compile_AL(code)
+    except Exception as e:
+        raise CompilationError(e)
+
+    _input(compiled)
 
 def model_delete(model_name):
     """Delete an existing model."""
@@ -395,16 +345,19 @@ def model_list(location):
     """List all models."""
     _goto_mode(MODE_MODELLING)
     _input(["model_list", location])
-    return set(_handle_output("Success: ", split=True))
+    return set(_handle_output("Success: ", split=" ").split("\n"))
 
 def model_list_full(location):
     """List full information on all models."""
     _goto_mode(MODE_MODELLING)
     _input(["model_list_full", location])
-    output = _handle_output("Success: ", split=True)
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
 
     lst = set([])
-    for v in output:
+    value = output.strip().split("\n")
+    for v in value:
         m = v.strip()
         perm, own, grp, m = m.split(" ", 3)
         lst.add((m, own, grp, perm))
@@ -419,15 +372,23 @@ def verify(model_name, metamodel_name=None):
         metamodel_name = _get_metamodel(model_name)
 
     _input(["verify", model_name, metamodel_name])
-    return _handle_output("Success: ", split=True)[0]
+    return _handle_output("Success: ", split=" ")
 
-def model_overwrite(model_name, new_model="", metamodel_name=None):
+def model_overwrite(model_name, new_model=None, metamodel_name=None):
     """Upload a new model and overwrite an existing model."""
     _goto_mode(MODE_MODIFY, model_name)
 
+    if new_model is not None:
+        try:
+            compiled = _compile_model(new_model)
+        except Exception as e:
+            raise CompilationError(e)
+    else:
+        compiled = [0]
+
     _input("upload")
     _handle_output("Waiting for model constructors...")
-    _input(new_model)
+    _input(compiled)
     _output("Success")
 
     if metamodel_name is not None:
@@ -453,19 +414,30 @@ def model_render(model_name, mapper_name):
     _goto_mode(MODE_MODELLING)
 
     _input(["model_render", model_name, mapper_name])
-    return json.loads(_handle_output("Success: ", split=True)[0])
+    return json.loads(_handle_output("Success: ", split=" "))
 
 def transformation_between(source, target):
     _goto_mode(MODE_MODELLING)
 
     _input(["transformation_between", source, target])
-    output = _handle_output("Success: ", split=True)
-    return set(output)
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
+    return set([v for v in output.split("\n")])
 
 def transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None):
     """Create a new model transformation."""
     global mode
     _goto_mode(MODE_MODELLING)
+    import time
+
+    start = time.time()
+    try:
+        compiled = _compile_model(code)
+    except Exception as e:
+        raise CompilationError(e)
+    #print("Compilation took: %ss" % (time.time() - start))
+    start = time.time()
 
     mv_dict_rep = _dict_to_list(source_metamodels) + [""] + _dict_to_list(target_metamodels) + [""]
     _input(["transformation_add_MT"] + mv_dict_rep + [operation_name])
@@ -477,17 +449,25 @@ def transformation_add_MT(source_metamodels, target_metamodels, operation_name,
         callback()
         _input("exit")
         mode = MODE_MODELLING
+    #print("Callbacks took: %ss" % (time.time() - start))
+    start = time.time()
 
     # Done, so RAMify and upload the model
     _handle_output("Waiting for model constructors...")
-    _input(code)
+    _input(compiled)
     _handle_output("Success")
+    #print("Upload and RAMify took: %ss" % (time.time() - start))
 
 def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None):
     """Create a new action language model, which can be executed."""
     global mode
     _goto_mode(MODE_MODELLING)
 
+    try:
+        compiled = _compile_AL(code)
+    except Exception as e:
+        raise CompilationError(e)
+
     mv_dict_rep = _dict_to_list(source_metamodels) + [""] + _dict_to_list(target_metamodels) + [""]
     _input(["transformation_add_AL"] + mv_dict_rep + [operation_name])
 
@@ -500,7 +480,7 @@ def transformation_add_AL(source_metamodels, target_metamodels, operation_name,
         mode = MODE_MODELLING
 
     _handle_output("Waiting for code constructors...")
-    _input(code)
+    _input(compiled)
     _output("Success")
 
 def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=lambda: None):
@@ -521,7 +501,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, statechart=None):
+def transformation_execute_AL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
     """Execute an existing model operation."""
     global mode
     _goto_mode(MODE_MODELLING)
@@ -531,21 +511,19 @@ 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")
 
-    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
+    # 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"]:
         mode = MODE_DIALOG
-        _exec_on_statechart(statechart)
+        reply = callback(_last_output())
         mode = MODE_MODELLING
-        return None
+        if reply is not None:
+            _input(reply)
+
+    # Got termination message, so we are done!
+    if _last_output() == "Success":
+        return True
     else:
-        # 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
+        return False
 
 def transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
     """Execute an existing model operation."""
@@ -574,7 +552,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, statechart=None):
+def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
     """Execute an existing model operation."""
     global mode
     _goto_mode(MODE_MODELLING)
@@ -584,31 +562,32 @@ 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")
 
-    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
+    # 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"]:
         mode = MODE_DIALOG
-        _exec_on_statechart(statechart)
+        reply = callback(_last_output())
         mode = MODE_MODELLING
-        return None
+        if reply is not None:
+            _input(reply)
+
+    # Got termination message, so we are done!
+    if _last_output() == "Success":
+        return True
     else:
-        # 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
+        return False
 
 def transformation_list():
     """List existing model operations."""
     _goto_mode(MODE_MODELLING)
 
     _input("transformation_list")
-    output = _handle_output("Success: ", split=True)
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
 
     lst = set([])
-    for v in output:
+    value = output.strip().split("\n")
+    for v in value:
         t, m = v.strip().split(" ", 1)
         t = t[1:-1].strip()
         m = m.strip().split(":")[0].strip()
@@ -730,8 +709,10 @@ def element_list(model_name):
 
     _input("list_full")
     lst = set([])
-    output = _handle_output("Success: ", split=True)
-    for v in output:
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
+    for v in output.split("\n"):
         m, mm = v.split(":")
         m = m.strip()
         mm = mm.strip()
@@ -744,8 +725,10 @@ def types(model_name):
 
     _input("types")
     lst = set([])
-    output = _handle_output("Success: ", split=True)
-    for v in output:
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
+    for v in output.split("\n"):
         m, mm = v.split(":")
         m = m.strip()
         lst.add(m)
@@ -757,8 +740,10 @@ def types_full(model_name):
 
     _input("types")
     lst = set([])
-    output = _handle_output("Success: ", split=True)
-    for v in output:
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
+    for v in output.split("\n"):
         m, mm = v.split(":")
         m = m.strip()
         mm = mm.strip()
@@ -770,7 +755,8 @@ def read(model_name, ID):
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read", ID])
-    v = _handle_output("Success: ", split=True)
+    output = _handle_output("Success: ", split=" ")
+    v = output.split("\n")
     t = v[1].split(":")[1].strip()
     if (not v[2].startswith("Source:")):
         rval = (t, None)
@@ -785,10 +771,11 @@ def read_attrs(model_name, ID):
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read", ID])
-    output = _handle_output("Success: ", split=True)
+    output = _handle_output("Success: ", split=" ")
+    v = output.split("\n")
     searching = True
     rval = {}
-    for r in output:
+    for r in v:
         if searching:
             if r == "Attributes:":
                 # Start working on attributes
@@ -817,7 +804,7 @@ def instantiate(model_name, typename, edge=None, ID=""):
         _input(["instantiate_node", typename, ID])
     else:
         _input(["instantiate_edge", typename, ID, edge[0], edge[1]])
-    return _handle_output("Success: ", split=True)[0]
+    return _handle_output("Success: ", split=" ")
 
 def delete_element(model_name, ID):
     """Delete the element with the given ID"""
@@ -837,12 +824,16 @@ def attr_assign(model_name, ID, attr, value):
 def attr_assign_code(model_name, ID, attr, code):
     """Assign a piece of Action Language code to the attribute"""
     _check_type(code)
+    try:
+        compiled = _compile_AL(code)
+    except Exception as e:
+        raise CompilationError(e)
 
     _goto_mode(MODE_MODIFY, model_name)
 
-    _input(["attr_add_code", ID, attr])
+    _input(["attr_add", ID, attr])
     _handle_output("Waiting for code constructors...")
-    _input(code)
+    _input(compiled)
     _output("Success")
 
 def attr_delete(model_name, ID, attr):
@@ -857,47 +848,45 @@ def read_outgoing(model_name, ID, typename):
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read_outgoing", ID, typename])
-    output = _handle_output("Success: ", split=True)
-    return set(output)
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
+    else:
+        return set(output.split("\n"))
 
 def read_incoming(model_name, ID, typename):
     """Returns a list of all incoming associations of a specific type ("" = all)"""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read_incoming", ID, typename])
-    output = _handle_output("Success: ", split=True)
-    return set(output)
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
+    else:
+        return set(output.split("\n"))
 
 def read_association_source(model_name, ID):
     """Returns the source of an association."""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read_association_source", ID])
-    return _handle_output("Success: ", split=True)[0]
+    return _handle_output("Success: ", split=" ")
 
 def read_association_destination(model_name, ID):
     """Returns the destination of an association."""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read_association_destination", ID])
-    return _handle_output("Success: ", split=True)[0]
+    return _handle_output("Success: ", split=" ")
 
 ##### To document:
 
 def service_register(name, function):
     """Register a function as a service with a specific name."""
 
-    def service_process(service_task):
-        global address
-        global port
-
-        ctrl = _start_http_client(address, port, 10.0) 
-        _listen_to_output(ctrl, service_task)
+    def service_process(port):
         while 1:
-            client_task = service_get(service_task)
-            ctrl = _start_http_client(address, port, 10.0) 
-            _listen_to_output(ctrl, client_task)
-            thrd = threading.Thread(target=function, args=[client_task])
+            thrd = threading.Thread(target=function, args=[service_get(port)])
             thrd.daemon = True
             thrd.start()
 
@@ -908,12 +897,10 @@ def service_register(name, function):
 
     # Now we are in service-mode
     mode = MODE_SERVICE
-    task = _handle_output("Success: ", split=True)[0]
+    port = _handle_output("Success: ", split=" ")
 
     # Process events in the background!
-    thrd = threading.Thread(target=service_process, args=[task])
-    thrd.daemon = True
-    thrd.start()
+    threading.Thread(target=service_process, args=[port]).start()
 
 def service_stop():
     """Stop the currently executing process."""
@@ -924,19 +911,18 @@ def service_stop():
     global mode
     mode = MODE_MODELLING
 
-def service_get(task):
-    """Get the values on the specified task."""
+def service_get(port):
+    """Get the values on the specified port."""
     _goto_mode(MODE_SERVICE)
 
-    val = _output(task=task)
-    return val
+    return _output(port=port)
 
-def service_set(task, value):
-    """Set a value on a specified task."""
+def service_set(port, value):
+    """Set a value on a specified port."""
     _check_type_list(value)
     _goto_mode(MODE_SERVICE)
 
-    _input(value, task=task)
+    _input(value, port=port)
 
 def user_password(user, password):
     """Change a user's password."""
@@ -952,7 +938,7 @@ def element_list_nice(model_name):
 
     _input(["element_list_nice", model_name, _get_metamodel(model_name)])
 
-    data = _handle_output("Success: ", split=True)[0]
+    data = _handle_output("Success: ", split=" ")
     try:
         return json.loads(data)
     except:
@@ -964,23 +950,29 @@ def connections_between(model_name, source_element, target_element):
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["connections_between", source_element, target_element])
-    output = _handle_output("Success: ", split=True)
-    return set(output)
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
+    else:
+        return set(output.split("\n"))
 
 def define_attribute(model_name, node, attr_name, attr_type):
     """Create a new attribute, which can be instantiated one meta-level below."""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["define_attribute", node, attr_name, attr_type])
-    return _handle_output("Success: ", split=True)[0]
+    return _handle_output("Success: ", split=" ")
 
 def all_instances(model_name, type_name):
     """Returns a list of all elements of a specific type."""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["all_instances", type_name])
-    output = _handle_output("Success: ", split=True)
-    return set(output)
+    output = _handle_output("Success: ", split=" ")
+    if output == "":
+        return set([])
+    else:
+        return set(output.split("\n"))
 
 def service_poll(port):
     """Checks whether or not the Modelverse side has any input ready to be processed."""
@@ -1004,20 +996,6 @@ def add_conformance(model_name, metamodel_name, partial_type_mapping=None):
     _handle_output("Success")
 
 def folder_create(folder_name):
-    """Create a new folder."""
     _goto_mode(MODE_MODELLING)
     _input(["folder_create", folder_name])
     _handle_output("Success")
-
-def model_types(model_name):
-    """Fetch all typings defined for this specific model."""
-    _goto_mode(MODE_MODELLING)
-    _input(["model_types", model_name])
-    output = _handle_output("Success: ", split=True)
-    return set(output)
-
-import atexit
-def _close_model():
-    if mode == MODE_MODIFY:
-        _model_exit()
-atexit.register(_close_model)