瀏覽代碼

First draft to allow for multiple tasks in DEVS model

Yentl Van Tendeloo 7 年之前
父節點
當前提交
fcc6ca46bb

二進制
bootstrap/bootstrap.m.gz


+ 22 - 11
bootstrap/compiler.alc

@@ -21,21 +21,32 @@ Element function generic_compile(code : String, port : String):
 		return read_root()!
 
 Element function compile_code(code : String):
-	String port
-	port = ""
-	while (port == ""):
-		port = comm_connect("compiler")
+	if (code == "__LOCAL__"):
+		Element list
+		Integer length
 
-	comm_set(port, "code")
+		list = list_create()
+		length = input()
 
-	Element list
-	list = generic_compile(code, port)
+		while (list_len(list) < length):
+			list_append(list, input())
+		
+		return construct_function_list(list)!
+	else:
+		String port
+		port = ""
+		while (port == ""):
+			port = comm_connect("compiler")
 
-	if (element_eq(list, read_root())):
-		return read_root()!
+		comm_set(port, "code")
 
-	else:
-		return construct_function_list(list)!
+		Element list
+		list = generic_compile(code, port)
+
+		if (element_eq(list, read_root())):
+			return read_root()!
+		else:
+			return construct_function_list(list)!
 
 Element function compile_model(code : String, metamodel : Element):
 	if (code == "__LOCAL__"):

+ 2 - 1
bootstrap/core_algorithm.alc

@@ -2434,7 +2434,8 @@ Void function user_function_skip_init(user_id : String):
 			return !
 		elif (cmd == "user_logout"):
 			// TODO
-			cmd = "FAIL"
+			output("Success")
+			return !
 		elif (cmd == "exit"):
 			// Exit by actually removing the user and decoupling it from all of its models
 			// Restarting with the same user name will NOT grant you access to anything of the previous user with that same name

+ 1 - 0
integration/test_powerwindow.py

@@ -113,6 +113,7 @@ class TestPowerWindow(unittest.TestCase):
         if called != 6:
             print(called)
             raise Exception("Not executed sufficiently:" + str(called))
+        user_logout()
 
     @slow
     def test_process_powerwindow_debug(self):

+ 109 - 19
model/model.py

@@ -1,5 +1,6 @@
 import sys
 import time
+import uuid
 sys.path.append("kernel/")
 sys.path.append("state/")
 sys.path.append("interface/HUTN")
@@ -338,7 +339,7 @@ class ModelverseKernel(AtomicDEVS):
             outputs[self.to_mvs] = [self.state.commands]
 
         if self.state.mvk and self.state.mvk.returnvalue is not None:
-            outputs[self.to_mvi] = [self.state.mvk.returnvalue]
+            outputs[self.to_mvi] = [(self.state.current_task, self.state.mvk.returnvalue)]
 
         return outputs
 
@@ -355,16 +356,24 @@ class ModelverseKernel(AtomicDEVS):
 class MvIState():
     def __init__(self):
         self.operations = []
-        self.output = []
-        self.processing = []
-        self.init = True
+        self.additional_operations = []
+        self.keyed_operations = {}
+        self.output = {}
+        self.allow_progress = True
+        self.blocked = True
         self.finished = False
+        self.send_operations = []
 
 class ModelverseInterface(AtomicDEVS):
-    def __init__(self, taskname, operations, finish_on):
+    def __init__(self, taskname, operations, finish_on, additional_operations=[], keyed_operations={}):
         AtomicDEVS.__init__(self, "MvI_%s" % taskname)
         self.state = MvIState()
+        if taskname == "task_manager":
+            self.state.blocked = False
         self.state.operations = operations
+        self.state.additional_operations = additional_operations
+        self.state.keyed_operations = keyed_operations
+        self.state.create_additional_task = False
         self.taskname = taskname
         self.finish_on = finish_on
 
@@ -372,31 +381,90 @@ class ModelverseInterface(AtomicDEVS):
         self.from_mvk = self.addInPort("from_MvK")
 
     def intTransition(self):
-        self.state.init = False
-        self.state.operations = []
+        self.state.create_additional_task = False
+        if self.state.allow_progress:
+            self.state.operations.pop(0)
+        elif not self.state.allow_progress:
+            self.state.send_operations.pop(0)
+
+        self.state.blocked = True
         return self.state
 
     def extTransition(self, inputs):
         for inp in inputs[self.from_mvk]:
-            self.state.output.append(inp)
-            if inp == self.finish_on:
+            print("Got input: " + str(inp))
+            self.state.blocked = False
+
+            self.state.output.setdefault(inp[0], []).append(inp[1])
+            if inp[0] == self.taskname and inp[1] == self.finish_on:
                 self.state.finished = True
-        print("Event history: " + str(self.state.output))
+            elif inp[0] == self.taskname and self.state.operations[0] is None:
+                # We have to block for now, and modify a model first...
+                print("Blocking for now...")
+                self.state.allow_progress = False
+                prev_output = self.state.output[inp[0]][-1]
+                if prev_output.startswith("Please edit this model before sending next input: "):
+                    _, model_name = prev_output.split("Please edit this model before sending next input: ", 1)
+                    self.state.do_additional_operations = True
+                    self.state.send_operations = [[], ["admin"], ["admin"], [], ["model_modify"], [model_name], ["formalisms/SimpleClassDiagrams"]] + self.state.additional_operations[0] + [["exit"]]
+                    self.state.additional_taskname = str(uuid.uuid4())
+                    print("Modifying model " + str(model_name))
+                    print("On taskname " + str(self.state.additional_taskname))
+                    self.state.create_additional_task = True
+                    self.state.had_key = None
+                elif prev_output.startswith("Spawned activity on task: "):
+                    print("Spawned activity")
+                    _, task_name = prev_output.split("Spawned activity on task: ", 1)
+                    self.state.blocked = True
+
+            elif inp[0] != self.taskname:
+                # Got some output on another task
+                if self.state.send_operations == []:
+                    # At the end of these operations, so finish up!
+                    if self.state.had_key is not None:
+                        self.state.keyed_operations.pop(self.state.had_key)
+                        self.state.additional_taskname = self.state.had_key
+                        self.state.additional_operations.insert(0, [])
+                        self.state.send_operations = ["__continue__"]
+                        self.state.had_key = None
+                    else:
+                        self.state.additional_operations.pop(0)
+                        self.state.do_additional_operations = False
+                        self.state.allow_progress = True
+                        self.state.send_operations = None
+                        self.state.operations[0] = []
+                        print("Clear up to the normal task!")
+                elif inp[1].startswith("Please edit this model before sending next input: "):
+                    _, model_name = inp[1].split("Please edit this model before sending next input: ", 1)
+                    self.state.do_additional_operations = True
+                    self.state.send_operations = [[], ["admin"], ["admin"], [], ["model_modify"], [model_name], ["formalisms/SimpleClassDiagrams"]] + self.state.keyed_operations[inp[0]] + [["exit"]]
+                    self.state.additional_taskname = str(uuid.uuid4())
+                    print("Modifying model " + str(model_name))
+                    print("On taskname " + str(self.state.additional_taskname))
+                    self.state.create_additional_task = True
+                    self.state.had_key = inp[0]
+
         return self.state
 
     def outputFnc(self):
-        if self.state.operations:
-            return {self.to_mvk: [(self.taskname, self.state.operations)]}
+        if self.state.allow_progress and self.state.operations:
+            print("REQUEST: " + str(self.state.operations[0]))
+            return {self.to_mvk: [(self.taskname, self.state.operations[0])]}
+        elif not self.state.allow_progress and self.state.send_operations:
+            outp = []
+            if self.state.create_additional_task:
+                outp.append(('task_manager', [self.state.additional_taskname]))
+            outp.append((self.state.additional_taskname, self.state.send_operations[0]))
+            print("REQUEST special: " + str(outp))
+            return {self.to_mvk: outp}
         else:
             return {}
 
     def timeAdvance(self):
-        if self.state.init:
-            return 0
-        elif self.state.processing:
-            return 0
-        else:
+        if self.state.blocked:
             return float("inf")
+        else:
+            return 0.0
 
 class NetworkState(object):
     def __init__(self):
@@ -444,6 +512,8 @@ class System(CoupledDEVS):
     def __init__(self,
                 taskname,
                 operations,
+                mvi_additional,
+                mvi_keyed,
                 finish_on,
                 rule_generation,
                 time_per_phase,
@@ -476,13 +546,15 @@ class System(CoupledDEVS):
 
         self.mvi_manager = self.addSubModel(ModelverseInterface(\
                             taskname            = "task_manager",
-                            operations          = [taskname],
+                            operations          = [[taskname]],
                             finish_on           = None,
                         ))
         self.mvi = self.addSubModel(ModelverseInterface(\
                             taskname            = taskname,
                             operations          = operations,
                             finish_on           = finish_on,
+                            additional_operations = mvi_additional,
+                            keyed_operations    = mvi_keyed,
                         ))
         self.mvk = self.addSubModel(ModelverseKernel(\
                             time_per_phase     = time_per_phase,
@@ -540,13 +612,31 @@ class System(CoupledDEVS):
 
 taskname = "test_task"
 
-operations = ["admin", "admin", "model_add", "formalisms/SimpleClassDiagrams", "formalisms/PN"] + compile_model("models/petrinets.mvc") + ["model_list", "formalisms", "echo", "FINISHED"]
+operations = json.loads(open("model/operations", 'r').read())
+additional_operations = [[], # revise_req
+                         [], # revise_environment
+                         [], # revise_plant
+                         [], # revise_control
+                         [], # revise_query
+                         [], # revise_architecture
+                         [], # make_initial_models
+                         [["instantiate_edge", "Association", "PLANT2EPN_link", "PW_Plant/State", "Encapsulated_PetriNet/Place"], ["instantiate_edge", "Association", "PLANT2EPN_tlink", "PW_Plant/Transition", "Encapsulated_PetriNet/Transition"]], # plant_to_EPN
+                         [["instantiate_edge", "Association", "CTRL2EPN_link", "PW_Control/State", "Encapsulated_PetriNet/Place"], ["instantiate_edge", "Association", "CTRL2EPN_tlink", "PW_Control/Transition", "Encapsulated_PetriNet/Transition"]], # control_to_EPN
+                         [["instantiate_edge", "Association", "ENV2EPN_link", "PW_Environment/Event", "Encapsulated_PetriNet/Place"]], # environment_to_EPN
+                         [["instantiate_edge", "Association", "EPN2PN_place_link", "Encapsulated_PetriNet/Place", "PetriNet/Place"], ["instantiate_edge", "Association", "EPN2PN_transition_link", "Encapsulated_PetriNet/Transition", "PetriNet/Transition"]], # EPN_to_PN
+                         [], # reachability
+                         [], # bfs
+                         [], # merge
+                        ]
+keyed_operations = {}
 finish_on = "FINISHED"
 
 args = {
         "taskname":             taskname,
         "operations":           operations,
         "finish_on":            finish_on,
+        "mvi_additional":       additional_operations,
+        "mvi_keyed":            keyed_operations,
         "mvi2mvk_latency":      0.0000001,
         "mvi2mvk_bandwidth":    50000000000,
         "mvk2mvs_latency":      0.0000001,

+ 22 - 7
wrappers/classes/modelverse.xml

@@ -11,8 +11,7 @@
             self.current_ID = action["ID"]
             self.action_name = action["name"]
         </body>
-    </method>
-
+    </method> 
     <method name="dict_to_list">
         <parameter name="d"/>
         <body>
@@ -97,6 +96,7 @@
             self.registered_metamodel = {}
             self.inputs = {}
             self.finish_output_thread = False
+            self.input_log = []
         </body>
     </constructor>
 
@@ -144,6 +144,9 @@
                             <parameter expr='urllib.urlencode({"op": "set_input", "data": json.dumps(value), "taskname": self.taskname})'/>
                             <parameter expr='None'/>
                         </raise>
+                        <script>
+                            self.input_log.append(value)
+                        </script>
                     </transition>
 
                     <transition event="request" cond="not isinstance(value, type([]))" target=".">
@@ -152,6 +155,9 @@
                             <parameter expr='urllib.urlencode({"op": "set_input", "value": json.dumps(value), "taskname": self.taskname})'/>
                             <parameter expr='None'/>
                         </raise>
+                        <script>
+                            self.input_log.append([value])
+                        </script>
                     </transition>
 
                     <transition event="request_raw" target=".">
@@ -338,7 +344,7 @@
                         <state id="send_metadata">
                             <onentry>
                                 <raise event="request">
-                                    <parameter expr="['model_add', self.parameters[1], self.parameters[0], self.parameters[2]]"/>
+                                    <parameter expr="['model_add', self.parameters[1], self.parameters[0]] + ([self.parameters[2]] if not COMPILE_LOCAL else compile_model(self.parameters[2]))"/>
                                 </raise>
                             </onentry>
 
@@ -446,7 +452,7 @@
                         <state id="send_model">
                             <onentry>
                                 <raise event="request">
-                                    <parameter expr="self.parameters[1]"/>
+                                    <parameter expr="self.parameters[1] if not COMPILE_LOCAL else compile_model(self.parameters[1])"/>
                                 </raise>
                             </onentry>
 
@@ -474,6 +480,10 @@
 
                     <state id="user_logout">
                         <onentry>
+                            <script>
+                                import json
+                                print(json.dumps(self.input_log))
+                            </script>
                             <raise event="request">
                                 <parameter expr="'user_logout'"/>
                             </raise>
@@ -600,6 +610,7 @@
                                 <transition cond="self.expect_response_partial('Please edit this model before sending next input: ', pop=False)" target="../wait_for_user">
                                     <script>
                                         model = self.responses.pop(0).split(": ", 1)[1]
+                                        self.input_log.append(None)
                                     </script>
                                     <raise event="result">
                                         <parameter expr="model"/>
@@ -607,11 +618,12 @@
                                 </transition>
 
                                 <transition cond="self.expect_response('Waiting for code constructors...')" target="../upload_changes">
+                                    <!-- TODO Is this ever executed? -->
                                     <raise event="result">
                                         <parameter expr="None"/>
                                     </raise>
                                     <raise event="request">
-                                        <parameter expr="self.parameters[3]"/>
+                                        <parameter expr="self.parameters[3] if not COMPILE_LOCAL else compile_code(self.parameters[3])"/>
                                     </raise>
                                 </transition>
                             </state>
@@ -630,13 +642,13 @@
                             <state id="upload_changes">
                                 <transition cond="self.expect_response('Waiting for code constructors...')" target=".">
                                     <raise event="request">
-                                        <parameter expr="self.parameters[3]"/>
+                                        <parameter expr="self.parameters[3] if not COMPILE_LOCAL else compile_code(self.parameters[3])"/>
                                     </raise>
                                 </transition>
 
                                 <transition cond="self.expect_response('Waiting for model constructors...')" target=".">
                                     <raise event="request">
-                                        <parameter expr="self.parameters[3]"/>
+                                        <parameter expr="self.parameters[3] if not COMPILE_LOCAL else compile_model(self.parameters[3])"/>
                                     </raise>
                                 </transition>
 
@@ -1438,6 +1450,9 @@
                                     <raise event="result">
                                         <parameter expr="self.responses.pop(0).split(': ')[1]"/>
                                     </raise>
+                                    <script>
+                                        self.input_log.append(None)
+                                    </script>
                                 </transition>
 
                                 <transition cond="None in self.inputs and self.inputs[None]" target="../../../history">

+ 45 - 6
wrappers/modelverse_SCCD.py

@@ -1,7 +1,7 @@
 """
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 
-Date:   Mon Dec 18 13:49:08 2017
+Date:   Tue Dec 19 14:25:12 2017
 
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server
@@ -19,6 +19,38 @@ import urllib
 import json
 import sys
 
+COMPILE_LOCAL = True
+if COMPILE_LOCAL:
+    sys.path.append("interface/HUTN")
+    from hutn_compiler.compiler import main as do_compile
+
+    def clean_code(code):
+        if code == "":
+            return code
+
+        code_fragments = code.split("\n")
+        code_fragments = [i.rstrip() 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)
+        return code.encode('ascii', 'replace')
+
+    def compile_model(model):
+        temp_file = ".model_%s" % str(uuid.uuid4())
+        with open(temp_file, 'w') as f:
+            f.write(model)
+        compiled = do_compile(temp_file, "interface/HUTN/grammars/modelling.g", "M")
+        return ["__LOCAL__"] + compiled
+
+    def compile_code(code):
+        temp_file = ".code_%s" % str(uuid.uuid4())
+        with open(temp_file, 'w') as f:
+            f.write(code)
+        compiled = do_compile(temp_file, "interface/HUTN/grammars/actionlanguage.g", "CS")
+        return ["__LOCAL__"] + compiled
+
 # package "MvK Server"
 
 class Modelverse(RuntimeClassBase):
@@ -48,6 +80,7 @@ class Modelverse(RuntimeClassBase):
         self.registered_metamodel = {}
         self.inputs = {}
         self.finish_output_thread = False
+        self.input_log = []
     
     def user_defined_destructor(self):
         pass
@@ -1883,7 +1916,7 @@ class Modelverse(RuntimeClassBase):
         self.raiseInternalEvent(Event("request", None, [['model_list', self.parameters[0]]]))
     
     def _initialized_behaviour_operations_model_add_send_metadata_enter(self):
-        self.raiseInternalEvent(Event("request", None, [['model_add', self.parameters[1], self.parameters[0], self.parameters[2]]]))
+        self.raiseInternalEvent(Event("request", None, [['model_add', self.parameters[1], self.parameters[0]] + ([self.parameters[2]] if not COMPILE_LOCAL else compile_model(self.parameters[2]))]))
     
     def _initialized_behaviour_operations_model_delete_enter(self):
         self.raiseInternalEvent(Event("request", None, [['model_delete', self.parameters[0]]]))
@@ -1898,12 +1931,14 @@ class Modelverse(RuntimeClassBase):
         self.raiseInternalEvent(Event("request", None, [['model_overwrite', self.parameters[0], self.registered_metamodel[self.parameters[0]]]]))
     
     def _initialized_behaviour_operations_model_overwrite_send_model_enter(self):
-        self.raiseInternalEvent(Event("request", None, [self.parameters[1]]))
+        self.raiseInternalEvent(Event("request", None, [self.parameters[1] if not COMPILE_LOCAL else compile_model(self.parameters[1])]))
     
     def _initialized_behaviour_operations_disconnect_enter(self):
         self.raiseInternalEvent(Event("request", None, ['exit']))
     
     def _initialized_behaviour_operations_user_logout_enter(self):
+        import json
+        print(json.dumps(self.input_log))
         self.raiseInternalEvent(Event("request", None, ['user_logout']))
     
     def _initialized_behaviour_operations_user_delete_enter(self):
@@ -2272,6 +2307,7 @@ class Modelverse(RuntimeClassBase):
     def _initialized_http_mapper_init_0_exec(self, parameters):
         value = parameters[0]
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, self.http_clients[0], Event("HTTP_input", None, [urllib.urlencode({"op": "set_input", "data": json.dumps(value), "taskname": self.taskname}), None])]))
+        self.input_log.append(value)
     
     def _initialized_http_mapper_init_0_guard(self, parameters):
         value = parameters[0]
@@ -2280,6 +2316,7 @@ class Modelverse(RuntimeClassBase):
     def _initialized_http_mapper_init_1_exec(self, parameters):
         value = parameters[0]
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, self.http_clients[0], Event("HTTP_input", None, [urllib.urlencode({"op": "set_input", "value": json.dumps(value), "taskname": self.taskname}), None])]))
+        self.input_log.append([value])
     
     def _initialized_http_mapper_init_1_guard(self, parameters):
         value = parameters[0]
@@ -2510,6 +2547,7 @@ class Modelverse(RuntimeClassBase):
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_send_metadata_0_exec(self, parameters):
         model = self.responses.pop(0).split(": ", 1)[1]
+        self.input_log.append(None)
         self.raiseInternalEvent(Event("result", None, [model]))
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_send_metadata_0_guard(self, parameters):
@@ -2517,7 +2555,7 @@ class Modelverse(RuntimeClassBase):
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_send_metadata_1_exec(self, parameters):
         self.raiseInternalEvent(Event("result", None, [None]))
-        self.raiseInternalEvent(Event("request", None, [self.parameters[3]]))
+        self.raiseInternalEvent(Event("request", None, [self.parameters[3] if not COMPILE_LOCAL else compile_code(self.parameters[3])]))
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_send_metadata_1_guard(self, parameters):
         return self.expect_response('Waiting for code constructors...')
@@ -2530,13 +2568,13 @@ class Modelverse(RuntimeClassBase):
         return None in self.inputs and self.inputs[None]
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_upload_changes_0_exec(self, parameters):
-        self.raiseInternalEvent(Event("request", None, [self.parameters[3]]))
+        self.raiseInternalEvent(Event("request", None, [self.parameters[3] if not COMPILE_LOCAL else compile_code(self.parameters[3])]))
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_upload_changes_0_guard(self, parameters):
         return self.expect_response('Waiting for code constructors...')
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_upload_changes_1_exec(self, parameters):
-        self.raiseInternalEvent(Event("request", None, [self.parameters[3]]))
+        self.raiseInternalEvent(Event("request", None, [self.parameters[3] if not COMPILE_LOCAL else compile_model(self.parameters[3])]))
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_upload_changes_1_guard(self, parameters):
         return self.expect_response('Waiting for model constructors...')
@@ -2901,6 +2939,7 @@ class Modelverse(RuntimeClassBase):
     
     def _initialized_behaviour_wait_for_action_activity_OP_forwarding_1_exec(self, parameters):
         self.raiseInternalEvent(Event("result", None, [self.responses.pop(0).split(': ')[1]]))
+        self.input_log.append(None)
     
     def _initialized_behaviour_wait_for_action_activity_OP_forwarding_1_guard(self, parameters):
         return self.expect_response_partial('Please edit this model before sending next input: ', pop=False)

+ 32 - 0
wrappers/modelverse_SCCD.xml

@@ -12,6 +12,38 @@
         import urllib
         import json
         import sys
+
+        COMPILE_LOCAL = True
+        if COMPILE_LOCAL:
+            sys.path.append("interface/HUTN")
+            from hutn_compiler.compiler import main as do_compile
+
+            def clean_code(code):
+                if code == "":
+                    return code
+
+                code_fragments = code.split("\n")
+                code_fragments = [i.rstrip() 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)
+                return code.encode('ascii', 'replace')
+
+            def compile_model(model):
+                temp_file = ".model_%s" % str(uuid.uuid4())
+                with open(temp_file, 'w') as f:
+                    f.write(model)
+                compiled = do_compile(temp_file, "interface/HUTN/grammars/modelling.g", "M")
+                return ["__LOCAL__"] + compiled
+
+            def compile_code(code):
+                temp_file = ".code_%s" % str(uuid.uuid4())
+                with open(temp_file, 'w') as f:
+                    f.write(code)
+                compiled = do_compile(temp_file, "interface/HUTN/grammars/actionlanguage.g", "CS")
+                return ["__LOCAL__"] + compiled
     </top>
 
     <inport name="socket_in"/>