Browse Source

Merge branch 'testing' into MvK_rules

Yentl Van Tendeloo 7 years ago
parent
commit
e404716257
43 changed files with 3005 additions and 1232 deletions
  1. BIN
      bootstrap/bootstrap.m.gz
  2. 272 175
      bootstrap/core_algorithm.alc
  3. 1 1
      bootstrap/model_management.alc
  4. 7 2
      bootstrap/modelling.alc
  5. 2 2
      bootstrap/random.alc
  6. 0 4
      bootstrap/semi_primitives.alc
  7. 2 2
      bootstrap/transform.alc
  8. 2 1
      bootstrap/typing.alc
  9. 61 39
      integration/test_powerwindow.py
  10. 3 2
      interface/plot/main.py
  11. 67 0
      interface/simple_plot/main.py
  12. 2 0
      kernel/modelverse_kernel/compiled.py
  13. 8 8
      kernel/modelverse_kernel/main.py
  14. 1 1
      kernel/modelverse_kernel/primitives.py
  15. 3 0
      kernel/modelverse_kernel/request_handler.py
  16. 180 0
      models/CBD_mapper.alc
  17. 43 0
      models/cbd_alg_design.mvc
  18. 61 0
      models/cbd_design.mvc
  19. 61 0
      models/cbd_merge.alc
  20. 76 0
      models/cbd_partial_runtime.mvc
  21. 12 0
      models/cbd_restartSim.alc
  22. 76 0
      models/cbd_runtime.mvc
  23. 467 0
      models/cbd_simulate.alc
  24. 54 0
      models/cbd_spring.mvc
  25. 69 0
      models/cbd_toRuntime.alc
  26. 143 0
      models/pm_live_CBD.mvc
  27. 214 0
      models/render_OD.alc
  28. 184 0
      models/render_SCD.alc
  29. 86 20
      models/upload_models.py
  30. 1 0
      scripts/run_local_modelverse.py
  31. 0 1
      services/HUTN/main.py
  32. 0 1
      services/JSON/main.py
  33. 6 0
      services/LoLA/install.sh
  34. BIN
      services/LoLA/lola-2.0.tar.gz
  35. 40 0
      services/LoLA/main.py
  36. 8 2
      unit/log_output.py
  37. 7 1
      unit/log_output.xml
  38. 40 37
      unit/test_all.py
  39. 1 0
      unit/utils.py
  40. 156 225
      wrappers/classes/modelverse.xml
  41. 141 99
      wrappers/modelverse.py
  42. 447 608
      wrappers/modelverse_SCCD.py
  43. 1 1
      wrappers/poll_print.xml

BIN
bootstrap/bootstrap.m.gz


+ 272 - 175
bootstrap/core_algorithm.alc

@@ -14,12 +14,14 @@ include "conformance_finding.alh"
 include "typing.alh"
 include "compiler.alh"
 include "random.alh"
+include "io.alh"
 
 String core_model_location = "models/core"
 
 Element core = ?
 String current_user_id
 Element caches
+Element pm_tasks = ?
 
 String function get_foldername(name : String):
 	Element result
@@ -465,20 +467,6 @@ Boolean function check_conformance(model_id : String):
 
 	return True!
 
-Boolean function pm_finished(worklist : Element, pm : String):
-	Element finished
-	Integer cnt
-	Integer i
-	// Check if any of the "finish" elements are in the worklist
-	// If so, we can already finish, and therefore will stop immediately
-	finished = allInstances(pm, "Finish")
-	worklist = set_copy(worklist)
-	while (set_len(worklist) > 0):
-		// Check each finished element individually
-		if (set_in(finished, list_read(set_pop(worklist), 0))):
-			return True!
-	return False!
-
 Element function merge_models(models_dict : Element, operation_name : String):
 	core = import_node(core_model_location)
 	// 0) Find operation signature
@@ -642,7 +630,7 @@ Element function execute_operation(operation_id : String, input_models : Element
 	while (set_len(iter) > 0):
 		edge = set_pop(iter)
 		dict_add(output_metamodels, read_attribute(core, edge, "name"), full_name(readAssociationDestination(core, edge)))
-	
+
 	// 1) Find merged metamodel
 
 	exact_type = read_type(core, operation_id)
@@ -661,6 +649,10 @@ Element function execute_operation(operation_id : String, input_models : Element
 		while (set_len(keys) > 0):
 			key = set_pop(keys)
 			Element mm
+			if (get_entry_id(input_models[key]) == ""):
+				// Input model doesn't exist yet, so create
+				cmd_model_add(input_metamodels[key], input_models[key], "")
+				log("Creating non-existing input model")
 			mm = get_full_model(get_entry_id(input_models[key]), get_entry_id(input_metamodels[key]))
 			if (element_eq(mm, read_root())):
 				log("Signature mismatch in operation for tag " + key)
@@ -717,14 +709,10 @@ Element function execute_operation(operation_id : String, input_models : Element
 		while (get_entry_id(model_name) != ""):
 			model_name = ".tmp/" + random_string(20)
 		model_create(merged_model, model_name, merged_metamodel_id, "Model")
-		output("Operating on: " + cast_string(model_name))
+		log("Manual model created " + model_name + " : " + cast_string(read_attribute(core, merged_metamodel_id, "name")))
 		// We want to modify, so modify
-		if (modify(merged_model, True)):
-			// Overwrite the merged model
-			model_overwrite(merged_model, get_entry_id(model_name), merged_metamodel_id)
-		else:
-			// Reload the merged model
-			merged_model = get_full_model(get_entry_id(model_name), merged_metamodel_id)
+		do_spawn_modify(model_name, True)
+		merged_model = get_full_model(get_entry_id(model_name), merged_metamodel_id)
 		//model_delete(get_entry_id(model_name))
 		result = True
 	elif (exact_type == "ActionLanguage"):
@@ -790,17 +778,22 @@ Boolean function enact_action(pm : Element, element : String, mapping : Element)
 	Element output_mms
 	Element consumes_link
 	String name
-	String value
 	String elem_name
 	Element keys
 	String key
 	String consume
 	String produce
+	Element input_traceability_model
+	String output_traceability_name
+	String output_traceability_type
 	Element output_map
 
 	inputs = dict_create()
 	outputs = dict_create()
 	output_map = dict_create()
+	input_traceability_model = read_root()
+	output_traceability_name = read_root()
+	output_traceability_type = read_root()
 
 	// Read out the referenced element from the MvC
 	transformation_id = get_entry_id(read_attribute(pm, element, "name"))
@@ -809,8 +802,19 @@ Boolean function enact_action(pm : Element, element : String, mapping : Element)
 	lst = allOutgoingAssociationInstances(pm, element, "Consumes")
 	while (set_len(lst) > 0):
 		consume = set_pop(lst)
-		value = read_attribute(pm, readAssociationDestination(pm, consume), "name")
-		dict_add(inputs, read_attribute(pm, consume, "name"), mapping[value])
+		elem = readAssociationDestination(pm, consume)
+		type_name = read_attribute(pm, elem, "type")
+		elem_name = read_attribute(pm, elem, "name")
+		if (cast_string(read_attribute(pm, consume, "name")) == "__traceability"):
+			log("Using input traceability model!")
+			log("Mapping: " + cast_string(mapping[elem_name]))
+			log("Type: " + cast_string(type_name))
+			log("Entry mapping: " + get_entry_id(mapping[elem_name]))
+			log("Entry type: " + get_entry_id(type_name))
+			input_traceability_model = model_copy(get_full_model(get_entry_id(mapping[elem_name]), get_entry_id(type_name)))
+			output_traceability_type = type_name
+		else:
+			dict_add(inputs, read_attribute(pm, consume, "name"), mapping[elem_name])
 
 	// Find all output model names and their metamodel
 	lst = allOutgoingAssociationInstances(pm, element, "Produces")
@@ -819,33 +823,46 @@ Boolean function enact_action(pm : Element, element : String, mapping : Element)
 		elem = readAssociationDestination(pm, produce)
 		type_name = read_attribute(pm, elem, "type")
 		elem_name = read_attribute(pm, elem, "name")
-		dict_add(outputs, read_attribute(pm, produce, "name"), type_name)
-		dict_add(output_map, read_attribute(pm, produce, "name"), mapping[elem_name])
+		if (cast_string(read_attribute(pm, produce, "name")) == "__traceability"):
+			output_traceability_name = mapping[elem_name]
+		else:
+			dict_add(outputs, read_attribute(pm, produce, "name"), type_name)
+			dict_add(output_map, read_attribute(pm, produce, "name"), mapping[elem_name])
 
 	if read_type(core, transformation_id) == "ActionLanguage":
 		log(string_join("Enacting ActionLanguage: ", read_attribute(pm, element, "name")))
-		output(string_join("Enacting ActionLanguage: ", read_attribute(pm, element, "name")))
+		output("Success: ready for AL execution")
 	elif read_type(core, transformation_id) == "ManualOperation":
 		log(string_join("Enacting ManualOperation: ", read_attribute(pm, element, "name")))
-		output(string_join("Enacting ManualOperation: ", read_attribute(pm, element, "name")))
+		output("Success: ready for MANUAL execution")
 	else:
 		log(string_join("Enacting ModelTransformation: ", read_attribute(pm, element, "name")))
-		output(string_join("Enacting ModelTransformation: ", read_attribute(pm, element, "name")))
-		
-	result = execute_operation(transformation_id, inputs, read_root())
+		output("Success: ready for MT execution")
+
+	result = execute_operation(transformation_id, inputs, input_traceability_model)
 
 	if (element_eq(result, read_root())):
 		// Something went wrong!
+		output("Failure")
 		return False!
 	else:
+		// Check if we have to store traceability information
+		if (element_neq(output_traceability_name, read_root())):
+			// Yes, so store it
+			// Note that we have overwritten the previous input traceability model, which is why it was a copy
+			model_overwrite(input_traceability_model, get_entry_id(output_traceability_name), get_entry_id(output_traceability_type))
+			log("Writing output traceability model!")
+
 		keys = dict_keys(outputs)
 		while (set_len(keys) > 0):
 			key = set_pop(keys)
+			log("Overwriting model at " + key)
 			if (get_entry_id(output_map[key]) == ""):
 				// New model
 				model_create(result[key], output_map[key], get_entry_id(outputs[key]), "Model")
 			else:
 				model_overwrite(result[key], get_entry_id(output_map[key]), get_entry_id(outputs[key]))
+		output("Success")
 		return True!
 
 Element function PM_signature(pm : Element):
@@ -861,6 +878,18 @@ Element function PM_signature(pm : Element):
 
 	return result!
 
+Void function enact_PM_activity(activity_to_task : Element, task_to_result : Element, pm : Element, element : String, mapping : Element):
+	Boolean result
+	pm_tasks = activity_to_task
+	set_add(activity_to_task[element], get_taskname())
+	result = enact_action(pm, element, mapping)
+	dict_add_fast(task_to_result, get_taskname(), result)
+
+	while (other_has_output(get_taskname())):
+		sleep(1)
+
+	return!
+
 Void function enact_PM(pm : Element, mapping : Element):
 	Element worklist
 	String element
@@ -869,6 +898,7 @@ Void function enact_PM(pm : Element, mapping : Element):
 	Element tuple
 	Element counters
 	Element join_nodes
+	Element exec_nodes
 	Element keys
 	String key
 
@@ -889,6 +919,8 @@ Void function enact_PM(pm : Element, mapping : Element):
 			while (get_entry_id(mock_location) != ""):
 				mock_location = ".tmp/" + random_string(10)
 			dict_add(mapping, key, mock_location)
+			cmd_model_add(signature[key], mapping[key], "")
+			log("Mock location created " + cast_string(signature[key]) + " : " + cast_string(mapping[key]))
 			set_add(mock_locations, mock_location)
 
 	// Initialize Join counters
@@ -897,76 +929,117 @@ Void function enact_PM(pm : Element, mapping : Element):
 	while (set_len(join_nodes) > 0):
 		dict_add(counters, set_pop(join_nodes), 0)
 
+	// Initialize activity to task dictionary with empty sets
+	Element activity_to_task
+	Element task_to_activity
+	Element task_to_result
+
+	activity_to_task = dict_create()
+	task_to_activity = dict_create()
+	task_to_result = dict_create()
+	exec_nodes = allInstances(pm, "Exec")
+	while (set_len(exec_nodes) > 0):
+		dict_add(activity_to_task, set_pop(exec_nodes), set_create())
+
 	// Create the worklist with the Start instance as first element
 	worklist = set_create()
-	set_add_node(worklist, create_tuple(set_pop(allInstances(pm, "Start")), True))
-
-	while (bool_not(pm_finished(worklist, pm))):
-		// Pop a random element from the list and execute it
-		tuple = set_pop(worklist)
-		element = tuple[0]
-		result = tuple[1]
-
-		// Find the type (to see what to do with it)
-		//   this does not yet yield the type of transformation, if it is an Execution
-		type = read_type(pm, element)
-
-		if (type == "Start"):
-			// Initial node, just progress to the next elements
-			// Nothing to do here though, as we have just started
-			result = True
-		elif (type == "Finish"):
-			// Should be impossible, as we would have ended...
-			result = result
-		elif (type == "Fork"):
-			result = result
-		elif (type == "Join"):
-			// Only do this if all dependencies are fullfilled
-			// So add to the counter of this Join
-			dict_overwrite(counters, element, integer_addition(counters[element], 1))
-
-			// Now check whether we have enough tokens to execute the Join itself
-			Integer required
-			Integer got
-			required = set_len(allIncomingAssociationInstances(pm, element, "Next")) + set_len(allIncomingAssociationInstances(pm, element, "Else"))
-			got = counters[element]
-			if (got == required):
-				// Reset counter to 0
-				dict_overwrite(counters, element, 0)
-
-				// And continue
-			else:
-				// We haven't gotten all yet, so we wait (i.e., continue without adding Next link to worklist)
-				continue!
+	set_add(worklist, set_pop(allInstances(pm, "Start")))
+
+	// Keep on iterating until we reach finish
+	while (True):
+		// Check if there are PM elements to expand
+		if (set_len(worklist) > 0):
+			// There is something we can do, so do it!
+			element = set_pop(worklist)
 
-		elif (type == "Exec"):
-			// Execute a transformation
-			// This the difficult part!
+			// Find the type (to see what to do with it)
+			//   this does not yet yield the type of transformation, if it is an Execution
+			type = read_type(pm, element)
 
-			result = enact_action(pm, element, mapping)
-			output("Success")
+			// Some types have nothing to do, such as start and fork
+			// Therefore, they are not mentioned in the following conditional
 
-		elif (type == "Decision"):
-			// If the previous result is true, we add the normal one, otherwise the false one
-			// in this context, that means that if it is false, we should add it manually to the list, and then continue the simulation loop
-			if (bool_not(result)):
-				// Apparently it is False, so map this to the "Else" branch
-				set_add_node(worklist, create_tuple(set_pop(allAssociationDestinations(pm, element, "Else")), True))
-				continue!
-			else:
-				// Apparently it is True, so map this to the "Then" branch
-				set_add_node(worklist, create_tuple(set_pop(allAssociationDestinations(pm, element, "Then")), True))
+			if (type == "Finish"):
+				// We have finished, so terminate
+				log("Finished node reached!")
+				break!
+			elif (type == "Join"):
+				// Only do this if all dependencies are fullfilled
+				// So add to the counter of this Join
+				dict_overwrite(counters, element, integer_addition(counters[element], 1))
+
+				// Now check whether we have enough tokens to execute the Join itself
+				Integer required
+				Integer got
+				required = set_len(allIncomingAssociationInstances(pm, element, "Next")) + set_len(allIncomingAssociationInstances(pm, element, "Else"))
+				got = counters[element]
+				if (got == required):
+					// Reset counter to 0
+					dict_overwrite(counters, element, 0)
+
+					// And continue
+				else:
+					// We haven't gotten all yet, so we wait (i.e., continue without adding Next link to worklist)
+					continue!
+
+			elif (type == "Exec"):
+				// Execute a transformation
+				// This the difficult part!
+
+				Element args
+				String taskname
+				args = list_create()
+				list_append(args, activity_to_task)
+				list_append(args, task_to_result)
+				list_append(args, pm)
+				list_append(args, element)
+				list_append(args, mapping)
+				taskname = spawn(enact_PM_activity, args)
+				output(taskname + " : " + cast_string(read_attribute(pm, element, "name")))
+				dict_add(task_to_activity, taskname, element)
 				continue!
 
-		// We have finished the execution, so add all outgoing edges to the worklist
-		Element all_next
-		all_next = allAssociationDestinations(pm, element, "Next")
-		String next
-		while (set_len(all_next) > 0):
-			next = set_pop(all_next)
-			set_add_node(worklist, create_tuple(next, result))
+			// We have finished the execution, so add all outgoing edges to the worklist
+			Element all_next
+			all_next = allAssociationDestinations(pm, element, "Next")
+			String next
+			while (set_len(all_next) > 0):
+				next = set_pop(all_next)
+				set_add(worklist, next)
+		else:
+			// No new tasks to spawn, so go and check on all running activities
+			if (dict_len(task_to_result) > 0):
+				// There are outputs!
+				Element keys
+				String task
+
+				keys = dict_keys(task_to_result)
+				while (set_len(keys) > 0):
+					task = set_pop(keys)
+					result = task_to_result[task]
+
+					Element all_next
+					all_next = allAssociationDestinations(pm, task_to_activity[task], "Next")
+					String next
+					while (set_len(all_next) > 0):
+						next = set_pop(all_next)
+
+						if (read_type(pm, next) == "Decision"):
+							// Got decision node, so expand immediately
+							if (result):
+								set_add(worklist, set_pop(allAssociationDestinations(pm, next, "Then")))
+							else:
+								set_add(worklist, set_pop(allAssociationDestinations(pm, next, "Else")))
+						else:
+							// Other node, so just append for further processing
+							set_add(worklist, next)
 
-	// Reached a finish element, so stop
+					set_remove(activity_to_task[task_to_activity[task]], task)
+					dict_delete(task_to_result, task)
+					dict_delete(task_to_activity, task)
+			else:
+				// No finished activities either, so we sleep for some time
+				sleep(0.1)
 
 	// Remove all mock locations again
 	while (set_len(mock_locations) > 0):
@@ -1039,30 +1112,25 @@ String function cmd_model_add(type : String, name : String, code : String):
 		// Type exists
 		if (allow_read(current_user_id, type_id)):
 			// And is readable
-			if (get_entry_id(get_foldername(name)) != ""):
-				// Folder doesn't exist yet!
-				if (allow_write(current_user_id, get_entry_id(get_foldername(name)))):
-					// Folder is writable
-					if (get_entry_id(name) == ""):
-						// Model doesn't exist yet
-						Element mm
-						mm = get_full_model(type_id, get_entry_id("formalisms/SimpleClassDiagrams"))
-						if (element_eq(mm, read_root())):
-							return "Type is not typed by formalisms/SimpleClassDiagrams: " + type!
+			create_folders(current_user_id, get_foldername(name))
+			//TODO check if bottommost existing model was writable by us
 
-						new_model = compile_model(code, mm)
+			if (get_entry_id(name) == ""):
+				// Model doesn't exist yet
+				Element mm
+				mm = get_full_model(type_id, get_entry_id("formalisms/SimpleClassDiagrams"))
+				if (element_eq(mm, read_root())):
+					return "Type is not typed by formalisms/SimpleClassDiagrams: " + type!
 
-						if (element_eq(new_model, read_root())):
-							return "Compilation error"!
+				new_model = compile_model(code, mm)
 
-						model_create(new_model, name, type_id, "Model")
-						return "Success"!
-					else:
-						return "Model exists: " + name!
-				else:
-					return "Permission denied to folder: " + get_foldername(name)!
+				if (element_eq(new_model, read_root())):
+					return "Compilation error"!
+
+				model_create(new_model, name, type_id, "Model")
+				return "Success"!
 			else:
-				return "No such folder: " + get_foldername(name)!
+				return "Model exists: " + name!
 		else:
 			return "Permission denied to model: " + type!
 	else:
@@ -1306,6 +1374,90 @@ String function cmd_model_render(model_name : String, mapper_name : String, rend
 	else:
 		return "Model not found: " + model_name!
 
+Boolean function do_spawn_modify(model_name : String, write : Boolean):
+	output("Please edit this model before sending next input: " + model_name)
+	input()
+	return False!
+
+Boolean function do_spawn_activity(transformation_id : String, tracability_name : String, inputs : Element, outputs : Element, output_map : Element):
+	Element lst
+	Element returnvalue
+	String taskname
+
+	lst = list_create()
+	returnvalue = set_create()
+	list_append(lst, returnvalue)
+	list_append(lst, transformation_id)
+	list_append(lst, tracability_name)
+	list_append(lst, inputs)
+	list_append(lst, outputs)
+	list_append(lst, output_map)
+	taskname = spawn(spawn_activity, lst)
+
+	output("Spawned activity on task: " + taskname)
+
+	while (set_len(returnvalue) == 0):
+		sleep(0.1)
+
+	output("Finished task: " + taskname)
+	return cast_boolean(set_pop(returnvalue))!
+
+Void function spawn_activity(returnvalue : Element, transformation_id : String, tracability_name : String, inputs : Element, outputs : Element, output_map : Element):
+	Element result
+	Element keys
+	String key
+
+	if (read_type(core, transformation_id) == "ActionLanguage"):
+		output("Success: ready for AL execution")
+	elif (read_type(core, transformation_id) == "ManualOperation"):
+		output("Success: ready for MANUAL execution")
+	else:
+		output("Success: ready for MT execution")
+
+	// Do tracability
+	Element tracability_model
+	if (tracability_name != ""):
+		// Check if exists
+		if (get_entry_id(tracability_name) == ""):
+			// No, so create
+			tracability_model = instantiate_model(get_full_model(get_entry_id("formalisms/Tracability"), get_entry_id("formalisms/SimpleClassDiagrams")))
+			model_create(tracability_model, tracability_name, get_entry_id("formalisms/Tracability"), "Model")
+		else:
+			// Yes, so read out
+			tracability_model = get_full_model(get_entry_id(tracability_name), get_entry_id("formalisms/Tracability"))
+	else:
+		tracability_model = read_root()
+
+	result = execute_operation(transformation_id, inputs, tracability_model)
+
+	// Flush tracability again, just to be sure
+	if (tracability_name != ""):
+		model_overwrite(tracability_model, get_entry_id(tracability_name), get_entry_id("formalisms/Tracability"))
+
+	// Now write out the models again
+	if (element_eq(result, read_root())):
+		// Something went wrong!
+		set_add(returnvalue, False)
+		output("Failure")
+	else:
+		keys = dict_keys(outputs)
+		while (set_len(keys) > 0):
+			key = set_pop(keys)
+			
+			if (get_entry_id(outputs[key]) == ""):
+				// New model
+				model_create(result[key], outputs[key], get_entry_id(output_map[key]), "Model")
+			else:
+				model_overwrite(result[key], get_entry_id(outputs[key]), get_entry_id(output_map[key]))
+
+		set_add(returnvalue, True)
+		output("Success")
+
+	while (other_has_output(get_taskname())):
+		sleep(1)
+
+	return!
+
 String function cmd_transformation_execute(transformation_name : String, source_models : Element, target_models : Element, tracability_name : String):
 	// Execute a transformation, whatever type it is
 	// First we detect the type, so we know how to prepare for invocation
@@ -1322,9 +1474,6 @@ String function cmd_transformation_execute(transformation_name : String, source_
 	String target_model_name
 	String source_model_name
 	String source_model_ID
-	Element result
-	Element keys
-	String key
 	String assoc_name
 
 	transformation_id = get_entry_id(transformation_name)
@@ -1381,54 +1530,10 @@ String function cmd_transformation_execute(transformation_name : String, source_
 							else:
 								return "Permission denied to model: " + target_model_name!
 
-				if (read_type(core, transformation_id) == "ActionLanguage"):
-					output("Success: ready for AL execution")
-				elif (read_type(core, transformation_id) == "ManualOperation"):
-					output("Success: ready for MANUAL execution")
-				else:
-					output("Success: ready for MT execution")
-
-				// Do tracability
-				Element tracability_model
-				if (tracability_name != ""):
-					// Check if exists
-					if (get_entry_id(tracability_name) == ""):
-						// No, so create
-						tracability_model = instantiate_model(get_full_model(get_entry_id("formalisms/Tracability"), get_entry_id("formalisms/SimpleClassDiagrams")))
-						model_create(tracability_model, tracability_name, get_entry_id("formalisms/Tracability"), "Model")
-					else:
-						// Yes, so read out
-						tracability_model = get_full_model(get_entry_id(tracability_name), get_entry_id("formalisms/Tracability"))
+				if (do_spawn_activity(transformation_id, tracability_name, inputs, outputs, output_map)):
+					return "Success"!
 				else:
-					tracability_model = read_root()
-
-				result = execute_operation(transformation_id, inputs, tracability_model)
-
-				// Flush tracability again, just to be sure
-				if (tracability_name != ""):
-					model_overwrite(tracability_model, get_entry_id(tracability_name), get_entry_id("formalisms/Tracability"))
-
-				// Now write out the models again
-				if (element_eq(result, read_root())):
-					// Something went wrong!
 					return "Failure"!
-				else:
-					keys = dict_keys(outputs)
-					while (set_len(keys) > 0):
-						key = set_pop(keys)
-						log("Writing away model with key " + cast_string(key))
-						log("Output map: " + dict_to_string(output_map))
-						log("Outputs: " + dict_to_string(outputs))
-						
-						if (get_entry_id(outputs[key]) == ""):
-							// New model
-							log("New model!")
-							model_create(result[key], outputs[key], get_entry_id(output_map[key]), "Model")
-						else:
-							log("Existing model!")
-							model_overwrite(result[key], get_entry_id(outputs[key]), get_entry_id(output_map[key]))
-
-					return "Success"!
 			else:
 				return "Model is not executable: " + transformation_name!
 		else:
@@ -1690,11 +1795,8 @@ String function transformation_add(source_models : Element, target_models : Elem
 		if (dict_len(source_models) + dict_len(target_models) > 0):
 			merged_formalism = model_fuse(formalism_map)
 			model_create(merged_formalism, "merged/" + operation_name, get_entry_id("formalisms/SimpleClassDiagrams"), "Model")
-			output("Operating on: merged/" + operation_name)
-			if (modify(merged_formalism, True)):
-				model_overwrite(merged_formalism, get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
-			else:
-				merged_formalism = get_full_model(get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
+			do_spawn_modify("merged/" + operation_name, True)
+			merged_formalism = get_full_model(get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
 
 		if (operation_type == "manual"):
 			if (dict_len(source_models) + dict_len(target_models) == 0):
@@ -1840,11 +1942,8 @@ String function cmd_transformation_add_MT(source_models : Element, target_models
 
 	merged_formalism = model_fuse(to_ramify)
 	model_create(merged_formalism, "merged/" + operation_name, get_entry_id("formalisms/SimpleClassDiagrams"), "Model")
-	output("Operating on: merged/" + operation_name)
-	if (modify(merged_formalism, True)):
-		model_overwrite(merged_formalism, get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
-	else:
-		merged_formalism = get_full_model(get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
+	do_spawn_modify("merged/" + operation_name, True)
+	merged_formalism = get_full_model(get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
 	model_overwrite(merged_formalism, get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
 
 	ramified_metamodel = ramify(merged_formalism)
@@ -2357,9 +2456,7 @@ Void function user_function_skip_init(user_id : String):
 		elif (cmd == "folder_create"):
 			output(cmd_folder_create(single_input("Folder name?")))
 		elif (cmd == "add_conformance"):
-			log("Adding conformance relation...")
 			output(cmd_conformance_add(single_input("Model name?"), single_input("Metamodel name?")))
-			log("Added!")
 		elif (cmd == "remove_conformance"):
 			// TODO
 			cmd = "FAIL"

+ 1 - 1
bootstrap/model_management.alc

@@ -194,7 +194,6 @@ Element function model_join(models : Element, metamodel : Element, tracability_m
 			src_name = read_attribute(tracability_model, readAssociationSource(tracability_model, tracability_link), "name")
 			dst_name = read_attribute(tracability_model, readAssociationDestination(tracability_model, tracability_link), "name")
 
-			log("Drawing tracability link from " + src_name + " to " + dst_name)
 			type = read_attribute(tracability_model, tracability_link, "type")
 
 			// Now try to find all possible combinations
@@ -213,6 +212,7 @@ Element function model_join(models : Element, metamodel : Element, tracability_m
 						if (dict_in(new_model["model"], dst_model + dst_name)):
 							// Found a match for source and target, so create a link
 							new_name = instantiate_link(new_model, type, "", src_model + src_name, dst_model + dst_name)
+
 							if (new_name == ""):
 								log("ERROR: could not create a tracability link; ignoring")
 	return new_model!

+ 7 - 2
bootstrap/modelling.alc

@@ -449,15 +449,20 @@ Void function unset_attribute(model : Element, element : String, attribute : Str
 	String attr_type
 	Element attr_links
 	String attr_link
+	String attr_value
 	
 	attr_type = find_attribute_type(model, element, attribute)
 	attr_links = allOutgoingAssociationInstances(model, element, attr_type)
 
 	while (set_len(attr_links) > 0):
 		attr_link = set_pop(attr_links)
-		remove_type(model, reverseKeyLookup(model["model"], read_edge_dst(model["model"][attr_link])))
+		attr_value = readAssociationDestination(model, attr_link)
 		remove_type(model, attr_link)
-		dict_delete(model["model"], reverseKeyLookup(model["model"], read_edge_dst(model["model"][attr_link])))
+		
+		if (dict_in(model["model"], attr_value)):
+			remove_type(model, attr_value)
+			dict_delete(model["model"], attr_value)
+
 		delete_element(model["model"][attr_link])
 
 	return!

+ 2 - 2
bootstrap/random.alc

@@ -18,13 +18,13 @@ Float function random():
 	seed = integer_modulo(a * seed + c, m)
 
 	// The seed is the new value
-	return float_division(seed, m)!
+	return float_division(cast_float(seed), cast_float(m))!
 
 Integer function random_interval(a : Integer, b : Integer):
 	if (a == b):
 		return a!
 	else:
-		return cast_integer(random() * (b - a + 1) + a)!
+		return cast_integer(random() * cast_float(b - a + 1) + cast_float(a))!
 
 Element function random_choice(list : Element):
 	if (list_len(list) == 0):

+ 0 - 4
bootstrap/semi_primitives.alc

@@ -520,9 +520,6 @@ String function spawn(function : Element, arguments : Element):
 	// Additionally, we copy over all globals that we previously had
 	dict_add_fast(task_root, "globals", dict_copy(root[get_taskname()]["globals"]))
 
-	log("Setting IP to " + cast_value(function["body"]))
-	log("Has outputs: " + set_to_string(dict_keys(function)))
-
 	if (dict_in(function, "params")):
 		// And add the arguments to the symbol table
 		Element symbols
@@ -542,7 +539,6 @@ String function spawn(function : Element, arguments : Element):
 			dict_add(entry, "value", list_pop(arguments, 0))
 			t = create_edge(symbols, entry)
 			create_edge(t, param_dict[string_get(arg_names_call, arg_i)])
-			log("Adding to symbols: " + cast_value(string_get(arg_names_call, arg_i)))
 			arg_i = arg_i + 1
 
 	// Add this only at the end, as otherwise the task will already be detected and executed

+ 2 - 2
bootstrap/transform.alc

@@ -554,7 +554,7 @@ Boolean function transform_forall(host_model : Element, schedule_model : Element
 	Element mapping
 	Boolean result
 
-	//log("Executing rule: " + current)
+	log("Executing rule: " + current)
 
 	mappings = full_match(host_model, schedule_model, current, False)
 
@@ -563,7 +563,7 @@ Boolean function transform_forall(host_model : Element, schedule_model : Element
 	else:
 		result = False
 
-	//log("Matches in forall: " + cast_string(set_len(mappings)))
+	log("Matches in forall: " + cast_string(set_len(mappings)))
 	while (set_len(mappings) > 0):
 		mapping = set_pop(mappings)
 		RHS = set_pop(allAssociationDestinations(schedule_model, current, "RHSLink"))

+ 2 - 1
bootstrap/typing.alc

@@ -89,7 +89,8 @@ Void function new_type_mapping(model : Element):
 	return !
 
 Void function remove_type(model : Element, name : String):
-	dict_delete(model["type_mapping"], name)
+	if (dict_in(model["type_mapping"], name)):
+		dict_delete(model["type_mapping"], name)
 	
 	String elem
 	elem = cast_id(model["model"][name])

+ 61 - 39
integration/test_powerwindow.py

@@ -2,6 +2,8 @@ import unittest
 
 from utils import *
 import sys
+import os
+import shutil
 
 sys.path.append("wrappers")
 from modelverse import *
@@ -36,20 +38,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(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_CTRL2EPN(model):
+            instantiate(model, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link")
+            instantiate(model, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_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_PLANT2EPN(model):
+            instantiate(model, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link")
+            instantiate(model, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink")
 
-        def tracability_ENV2EPN(context):
-            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link", context=context)
+        def tracability_ENV2EPN(model):
+            instantiate(model, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_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)
+        def tracability_EPN2PN(model):
+            instantiate(model, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
+            instantiate(model, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
 
         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)
@@ -62,18 +64,26 @@ class TestPowerWindow(unittest.TestCase):
         transformation_add_AL({"ReachabilityGraph": "formalisms/ReachabilityGraph"}, {}, "models/bfs", open("models/bfs.alc", 'r').read())
         transformation_add_AL({"EPN_Plant": "formalisms/Encapsulated_PetriNet", "EPN_Control": "formalisms/Encapsulated_PetriNet", "EPN_Environment": "formalisms/Encapsulated_PetriNet"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/merge_EPN", open("models/merge_EPN.alc", 'r').read())
 
-        global called
-        called = 0
         nr_of_operations = 6
 
+        try:
+            shutil.rmtree(".TEST_POWER_WINDOW")
+        except:
+            pass
+        os.makedirs(".TEST_POWER_WINDOW")
+
         def get_function(filename):
-            def func(context):
-                global called
+            def func(model):
+                import uuid
+                import os
+                called = len(os.listdir(".TEST_POWER_WINDOW/"))
                 if called > nr_of_operations:
                     raise Exception("Seemingly called some operation twice!")
                 else:
-                    called += 1
-                model_overwrite(None, open(filename, "r").read(), context=context)
+                    f = open(".TEST_POWER_WINDOW/%s" % str(uuid.uuid4()), 'w')
+                    f.write(filename)
+                    f.close()
+                model_overwrite(model, open(filename, "r").read())
             return func
 
         cb_req = get_function("models/requirements_model.mvc")
@@ -98,6 +108,8 @@ class TestPowerWindow(unittest.TestCase):
             import traceback
             print(traceback.format_exc())
 
+        called = len(os.listdir(".TEST_POWER_WINDOW/"))
+        shutil.rmtree(".TEST_POWER_WINDOW")
         if called != 6:
             print(called)
             raise Exception("Not executed sufficiently:" + str(called))
@@ -124,20 +136,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(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_CTRL2EPN(model):
+            instantiate(model, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link")
+            instantiate(model, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_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_PLANT2EPN(model):
+            instantiate(model, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link")
+            instantiate(model, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink")
 
-        def tracability_ENV2EPN(context):
-            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link", context=context)
+        def tracability_ENV2EPN(model):
+            instantiate(model, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_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)
+        def tracability_EPN2PN(model):
+            instantiate(model, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
+            instantiate(model, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
 
         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)
@@ -153,27 +165,34 @@ class TestPowerWindow(unittest.TestCase):
         #transformation_add_AL({"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, {}, "epn_print", open("models/epn_print.alc").read())
         #transformation_add_AL({"PetriNet": "PetriNet"}, {}, "pn_print", open("models/pn_print.alc").read())
 
-        global called
-        called = 0
+        try:
+            shutil.rmtree(".TEST_POWER_WINDOW")
+        except:
+            pass
+        os.makedirs(".TEST_POWER_WINDOW")
+
         executed = set([])
 
         def get_function(filename, fixed=None):
-            def func(context):
-                global called
-                if called > len(callbacks) * 2:
+            def func(model):
+                import uuid
+                import os
+                nr_operations = 11
+                called = len(os.listdir(".TEST_POWER_WINDOW/"))
+                if (len(os.listdir(".TEST_POWER_WINDOW")) > nr_operations):
                     raise Exception("Seemingly called some operation twice!")
-                called += 1
-                if filename not in executed or fixed is None:
-                    executed.add(filename)
-                    model_overwrite(None, open(filename, "r").read(), context=context)
+                elif (len(glob.glob(".TEST_POWER_WINDOW/%s_*" % str(filename))) == 0) or fixed is None:
+                    model_overwrite(model, open(filename, "r").read())
                 else:
-                    model_overwrite(None, open(fixed, "r").read(), context=context)
+                    model_overwrite(model, open(fixed, "r").read())
+
+                open(".TEST_POWER_WINDOW/%s_%s" % str(filename, uuid.uuid4()), 'w').close()
             return func
 
         cb_req = get_function("models/requirements_model.mvc")
         cb_plant = get_function("models/plant_model.mvc")
         cb_env = get_function("models/environment_model.mvc")
-        cb_ctrl = get_function("models/control_model_wrong.mvc", "models/control_model.mvc")
+        cb_ctrl = get_function("models/control_model_wrong.mvc", fixed="models/control_model.mvc")
         cb_query = get_function("models/query_model.mvc")
         cb_arch = get_function("models/architecture_model.mvc")
 
@@ -198,6 +217,9 @@ class TestPowerWindow(unittest.TestCase):
 
         thrd.join()
 
+        called = len(os.listdir(".TEST_POWER_WINDOW"))
+        shutil.rmtree(".TEST_POWER_WINDOW")
+
         if called != 11:
             print(called)
             raise Exception("Not executed sufficiently:" + str(called))

+ 3 - 2
interface/plot/main.py

@@ -6,14 +6,15 @@ sys.path.append("wrappers/")
 from MatPlotLibWrapper import *
 from plot import *
 from modelverse import *
+import uuid
 
 # Login to Modelverse and have model rendered
 init()
 login("admin", "admin")
-rendered = model_render(sys.argv[1], sys.argv[2])
+rendered = model_render(sys.argv[1], "models/trace_mapper", "rendered/%s" % str(uuid.uuid4()))
 
 # Convert rendered to "MatPlotLib JSON"
-data = json.loads(rendered)
+data = rendered
 plots = {}
 axis = {}
 datasets = {}

+ 67 - 0
interface/simple_plot/main.py

@@ -0,0 +1,67 @@
+import sys
+import matplotlib.pyplot as plt
+import json
+import threading
+
+def read_data():
+    try:
+        return json.load(open("/tmp/values.pickle", 'r'))
+    except:
+        return (0.0, {})
+
+def write_data(values):
+    json.dump(values, open("/tmp/values.pickle", 'w'))
+
+plt.ion()
+plt.figure()
+
+old_time, d = read_data()
+plt.pause(0.01)
+l = sys.stdin.readline()
+time, _ = l.split(" ", 1)
+time = float(time)
+
+maps = {}
+
+if time <= old_time:
+    # Overwrites current values, so flush
+    d = {}
+else:
+    for key in d:
+        maps[key], = plt.plot(d[key][0], d[key][1])
+        maps[key].set_label(key)
+    plt.legend()
+
+first = l
+
+while 1:
+    if first is not None:
+        l = first
+        first = None
+    else:
+        l = sys.stdin.readline()
+
+    if l == "CLOSE":
+        import sys
+        sys.exit(0)
+    elif l == "ALGEBRAIC_LOOP":
+        continue
+    time, key, value = l.split(" ")
+    time = float(time)
+    value = float(value)
+
+    if key not in maps:
+        maps[key], = plt.plot([], [])
+        maps[key].set_label(key)
+        plt.legend()
+
+    if key not in d:
+        d[key] = ([], [])
+
+    d[key][0].append(time)
+    d[key][1].append(value)
+    maps[key].set_xdata(d[key][0])
+    maps[key].set_ydata(d[key][1])
+    plt.gca().set_xlim([min(d[key][0]), max(d[key][0])])
+    plt.pause(0.01)
+    write_data((0.0, d))

+ 2 - 0
kernel/modelverse_kernel/compiled.py

@@ -21,6 +21,8 @@ def reverseKeyLookupMulti(a, b, **remainder):
     raise PrimitiveFinished(result)
 
 def reverseKeyLookup(a, b, **remainder):
+    if a is None or b is None:
+        print("ERROR @ reverseKeyLookup: " + str(locals()))
     edges_out, edges_in = yield [("RO", [a]), ("RI", [b])]
     options = set(edges_out) & set(edges_in)
     if options:

+ 8 - 8
kernel/modelverse_kernel/main.py

@@ -293,10 +293,6 @@ class ModelverseKernel(object):
             print("Got roots: " + str(evalstack_roots))
             raise Exception("Could not process continue statement!")
 
-        # Remove the self.taskname link from the evalstack, as we will repush it
-        lnk, = yield [("RDE", [evalstack_root, self.taskname])]
-        yield [("DE", [lnk])]
-
         prev_evalstack_roots, old_evalstack_phase_link = \
                             yield [("RRD", [evalstack_root, "prev"]),
                                    ("RDE", [evalstack_root, "phase"]),
@@ -496,18 +492,22 @@ class ModelverseKernel(object):
                                    ("CNV", ["init"]),
                                    ("CNV", ["init"]),
                                   ]
-            _, _, _, _, _, _, _, _, _, _ = \
+            _, _, _, _, _, _, _, _, _ = \
                             yield [("CD", [task_frame, "IP", body]),
                                    ("CD", [task_frame, "phase", new_phase]),
                                    ("CD", [task_frame, "evalstack", new_evalstack]),
                                    ("CD", [new_evalstack, "prev", evalstack]),
                                    ("CD", [evalstack, "inst", inst]),
-                                   ("CD", [evalstack, self.taskname, inst]),
                                    ("CD", [evalstack, "phase", evalstack_phase]),
                                    ("DE", [evalstack_link]),
                                    ("DE", [ip_link]),
                                    ("DE", [phase_link]),
                                   ]
+
+            # Check if we already have a taskname link to the evalstack
+            links, = yield [("RD", [evalstack, self.taskname])]
+            if links is None:
+                yield [("CD", [evalstack, self.taskname, inst])]
         else:
             phase_link, new_phase = \
                             yield [("RDE", [task_frame, "phase"]),
@@ -702,6 +702,7 @@ class ModelverseKernel(object):
             if prev_frame is None:
                 _, =            yield [("DN", [task_root])]
                 del self.debug_info[self.taskname]
+                print("Cleanup task " + str(self.taskname))
             else:
                 if self.debug_info[self.taskname]:
                     self.debug_info[self.taskname].pop()
@@ -906,8 +907,7 @@ class ModelverseKernel(object):
                                    ("RD", [inst, "last_param"]),
                                   ]
 
-            body, =         yield [("RD", [new_IP, "body"])]
-            self.jit.mark_entry_point(body)
+            self.jit.mark_entry_point(new_IP)
             
             name, =         yield [("RD", [last_param, "name"])]
             name_value, =   yield [("RV", [name])]

+ 1 - 1
kernel/modelverse_kernel/primitives.py

@@ -208,7 +208,7 @@ def dict_delete(a, b, **remainder):
     b_value, = yield [("RV", [b])]
     edge, = yield [("RDE", [a, b_value])]
     if edge is None:
-        print("Failed dict_delete for value %s!" % b_value)
+        print("Failed dict_delete for value '%s'!" % b_value)
         keys, = yield [("RDK", [a])]
         keys = yield [("RV", [i]) for i in keys]
         print("Keys: " + str(keys))

+ 3 - 0
kernel/modelverse_kernel/request_handler.py

@@ -278,6 +278,9 @@ class RequestHandler(object):
         except primitive_functions.PrimitiveFinished:
             self.push_generator(None)
             raise
+        except:
+            print("EXCEPTION for " + str(locals()))
+            raise
 
     def execute_call_args(self, request_args):
         """Executes a CALL_ARGS-request with the given argument list."""

+ 180 - 0
models/CBD_mapper.alc

@@ -0,0 +1,180 @@
+include "primitives.alh"
+include "modelling.alh"
+include "object_operations.alh"
+include "utils.alh"
+
+Boolean function main(model : Element):
+	Element elements
+	String class
+	Element attrs
+	Element attr_keys
+	String attr_key
+	String group
+	String elem
+	Integer loc
+	Integer text_loc
+	Element related_groups
+	loc = 10
+
+	Element groups
+	groups = dict_create()
+
+	elements = allInstances(model, "rendered/Group")
+	while (set_len(elements) > 0):
+		group = set_pop(elements)
+		if (set_len(allIncomingAssociationInstances(model, group, "TracabilityClass")) == 0):
+			Element to_remove
+			String elem_to_remove
+			to_remove = allAssociationDestinations(model, group, "rendered/contains")
+			while (set_len(to_remove) > 0):
+				elem_to_remove = set_pop(to_remove)
+				if (read_type(model, elem_to_remove) == "rendered/Group"):
+					set_add(to_remove, elem_to_remove)
+				else:
+					model_delete_element(model, elem_to_remove)
+			model_delete_element(model, group)
+
+	elements = allInstances(model, "abstract/Block")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+		
+		Integer x
+		Integer y
+		x = loc
+		y = 10
+
+		// Check if there is already an associated element
+		if (set_len(allOutgoingAssociationInstances(model, class, "TracabilityClass")) > 0):
+			// Yes, but is it still clean?
+			Boolean dirty
+			dirty = False
+
+			related_groups = allAssociationDestinations(model, class, "TracabilityClass")
+			while (set_len(related_groups) > 0):
+				group = set_pop(related_groups)
+				if (value_eq(read_attribute(model, group, "dirty"), True)):
+					// No, so mark all as dirty
+					dirty = True
+					break!
+				else:
+					// Yes, so just ignore this!
+					continue!
+
+			if (bool_not(dirty)):
+				dict_add(groups, class, group)
+				continue!
+			else:
+				related_groups = allAssociationDestinations(model, class, "TracabilityClass")
+				Element to_remove
+				String elem_to_remove
+				while (set_len(related_groups) > 0):
+					group = set_pop(related_groups)
+					to_remove = allAssociationDestinations(model, group, "rendered/contains")
+					x = create_value(read_attribute(model, group, "x"))
+					y = create_value(read_attribute(model, group, "y"))
+					while (set_len(to_remove) > 0):
+						elem_to_remove = set_pop(to_remove)
+						if (read_type(model, elem_to_remove) == "rendered/Group"):
+							set_add(to_remove, elem_to_remove)
+						else:
+							model_delete_element(model, elem_to_remove)
+					model_delete_element(model, group)
+
+		attr_keys = dict_keys(getAttributeList(model, class))
+		text_loc = 5
+
+		group = instantiate_node(model, "rendered/Group", "")
+		instantiate_attribute(model, group, "x", x)
+		instantiate_attribute(model, group, "y", y)
+		instantiate_attribute(model, group, "__asid", list_read(string_split_nr(class, "/", 1), 1))
+		instantiate_attribute(model, group, "layer", 0)
+		dict_add(groups, class, group)
+		loc = loc + 100
+
+		elem = instantiate_node(model, "rendered/Rectangle", "")
+		instantiate_attribute(model, elem, "x", 0)
+		instantiate_attribute(model, elem, "y", 0)
+		instantiate_attribute(model, elem, "height", 50)
+		instantiate_attribute(model, elem, "width", 50)
+		instantiate_attribute(model, elem, "lineWidth", 4) 
+		instantiate_attribute(model, elem, "lineColour", "black")
+		instantiate_attribute(model, elem, "fillColour", "white")
+		instantiate_attribute(model, elem, "layer", 1)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+		elem = instantiate_node(model, "rendered/Text", "")
+		instantiate_attribute(model, elem, "x", 20)
+		instantiate_attribute(model, elem, "y", 20)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "black")
+		
+		String type
+		type = read_type(model, class)
+		if (type == "abstract/ConstantBlock"):
+			if (element_neq(read_attribute(model, class, "value"), read_root())):
+				instantiate_attribute(model, elem, "text", cast_string(read_attribute(model, class, "value")))
+			else:
+				instantiate_attribute(model, elem, "text", "?")
+		elif (type == "abstract/AdditionBlock"):
+			instantiate_attribute(model, elem, "text", "+")
+		elif (type == "abstract/NegatorBlock"):
+			instantiate_attribute(model, elem, "text", "-")
+		elif (type == "abstract/MultiplyBlock"):
+			instantiate_attribute(model, elem, "text", "X")
+		elif (type == "abstract/InverseBlock"):
+			instantiate_attribute(model, elem, "text", "1/x")
+		elif (type == "abstract/DelayBlock"):
+			instantiate_attribute(model, elem, "text", "DELAY")
+		elif (type == "abstract/IntegratorBlock"):
+			instantiate_attribute(model, elem, "text", "1/s")
+		elif (type == "abstract/DerivatorBlock"):
+			instantiate_attribute(model, elem, "text", "dx")
+		elif (type == "abstract/ProbeBlock"):
+			instantiate_attribute(model, elem, "text", "PROBE")
+
+		instantiate_attribute(model, elem, "layer", 2)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+		instantiate_link(model, "TracabilityClass", "", class, group)
+
+	// Flush all associations
+	elements = allInstances(model, "rendered/ConnectingLine")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+		model_delete_element(model, class)
+
+	// Rerender associations
+	elements = allInstances(model, "abstract/Link")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+
+		elem = instantiate_link(model, "rendered/ConnectingLine", "", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])
+		instantiate_attribute(model, elem, "offsetSourceX", 25)
+		instantiate_attribute(model, elem, "offsetSourceY", 25)
+		instantiate_attribute(model, elem, "offsetTargetX", 25)
+		instantiate_attribute(model, elem, "offsetTargetY", 25)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "black")
+		instantiate_attribute(model, elem, "arrow", True)
+		instantiate_attribute(model, elem, "__asid", list_read(string_split_nr(class, "/", 1), 1))
+		instantiate_attribute(model, elem, "layer", 0)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+	// Rerender initial conditions
+	elements = allInstances(model, "abstract/InitialCondition")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+
+		elem = instantiate_link(model, "rendered/ConnectingLine", "", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])
+		instantiate_attribute(model, elem, "offsetSourceX", 25)
+		instantiate_attribute(model, elem, "offsetSourceY", 25)
+		instantiate_attribute(model, elem, "offsetTargetX", 25)
+		instantiate_attribute(model, elem, "offsetTargetY", 25)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "red")
+		instantiate_attribute(model, elem, "arrow", True)
+		instantiate_attribute(model, elem, "__asid", list_read(string_split_nr(class, "/", 1), 1))
+		instantiate_attribute(model, elem, "layer", 0)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+	return True!

+ 43 - 0
models/cbd_alg_design.mvc

@@ -0,0 +1,43 @@
+include "primitives.alh"
+
+Class Float {}
+Class String {}
+
+Class Block{}
+
+Class ConstantBlock{
+    name = "Constant"
+    value : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Class AdditionBlock{
+    name = "Addition"
+}
+Class NegatorBlock{
+    name = "Negator"
+}
+Class MultiplyBlock{
+    name = "Multiply"
+}
+Class InverseBlock{
+    name = "Inverse"
+}
+Class ProbeBlock{
+    name = "Probe"
+    name : String {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Association Link(Block, Block){}
+
+Inheritance (ConstantBlock, Block){}
+Inheritance (AdditionBlock, Block){}
+Inheritance (NegatorBlock, Block){}
+Inheritance (MultiplyBlock, Block){}
+Inheritance (InverseBlock, Block){}
+Inheritance (ProbeBlock, Block){}

+ 61 - 0
models/cbd_design.mvc

@@ -0,0 +1,61 @@
+include "primitives.alh"
+
+Class Float {}
+Class String {}
+
+Class Block{}
+Class ICBlock{}
+
+Class ConstantBlock{
+    name = "Constant"
+    value : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Class AdditionBlock{
+    name = "Addition"
+}
+Class NegatorBlock{
+    name = "Negator"
+}
+Class MultiplyBlock{
+    name = "Multiply"
+}
+Class InverseBlock{
+    name = "Inverse"
+}
+Class DelayBlock{
+    name = "Delay"
+}
+Class IntegratorBlock{
+    name = "Integrator"
+}
+Class DerivatorBlock{
+    name = "Derivator"
+}
+Class ProbeBlock{
+    name = "Probe"
+    name : String {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Association Link(Block, Block){}
+Association InitialCondition(Block, ICBlock){
+    source_lower_cardinality = 1
+    source_upper_cardinality = 1
+}
+
+Inheritance (ConstantBlock, Block){}
+Inheritance (AdditionBlock, Block){}
+Inheritance (NegatorBlock, Block){}
+Inheritance (MultiplyBlock, Block){}
+Inheritance (InverseBlock, Block){}
+Inheritance (ProbeBlock, Block){}
+Inheritance (ICBlock, Block){}
+Inheritance (DelayBlock, ICBlock){}
+Inheritance (IntegratorBlock, ICBlock){}
+Inheritance (DerivatorBlock, ICBlock){}

+ 61 - 0
models/cbd_merge.alc

@@ -0,0 +1,61 @@
+include "primitives.alh"
+include "modelling.alh"
+include "object_operations.alh"
+include "conformance_scd.alh"
+include "utils.alh"
+include "typing.alh"
+include "mini_modify.alh"
+
+String function map_P2F(model : Element, name : String):
+	Element destinations
+	String pick
+
+	destinations = allAssociationDestinations(model, name, "P2F_block")
+
+	pick = name
+	while (pick == name):
+		pick = set_pop(destinations)
+
+	return pick!
+
+Boolean function main(model : Element):
+	Element all_blocks
+	String element_name
+	Float current_time
+	String time_block
+
+	all_blocks = allInstances(model, "PartialRuntime/Block")
+	while (set_len(all_blocks) > 0):
+		element_name = set_pop(all_blocks)
+		if (set_len(allOutgoingAssociationInstances(model, element_name, "P2F_block")) > 0):
+			// Element already exists in full, so copy existing attributes
+            if (is_nominal_instance(model, element_name, "PartialRuntime/ICBlock")):
+                instantiate_attribute(model, element_name, "last_in", read_attribute(model, map_P2F(model, element_name), "last_in"))
+            if (is_nominal_instance(model, element_name, "PartialRuntime/IntegratorBlock")):
+                instantiate_attribute(model, element_name, "last_out", read_attribute(model, map_P2F(model, element_name), "last_out"))
+            instantiate_attribute(model, element_name, "signal", read_attribute(model, map_P2F(model, element_name), "signal"))
+		else:
+			// Element doesn't exist, so initialize with 0.0
+            instantiate_attribute(model, element_name, "signal", 0.0)
+			instantiate_link(model, "P2F_block", "", element_name, element_name)
+
+	if (set_len(allInstances(model, "FullRuntime/Time")) > 0):
+		// Time already exists, so copy the value
+		current_time = read_attribute(model, set_pop(allInstances(model, "FullRuntime/Time")), "current_time")
+	else:
+		// No time yet, so initialize
+		current_time = 0.0
+
+	time_block = instantiate_node(model, "PartialRuntime/Time", "")
+	instantiate_attribute(model, time_block, "start_time", current_time)
+	instantiate_attribute(model, time_block, "current_time", current_time)
+
+	Element all_elements
+	String elem
+	all_elements = dict_keys(model["model"])
+	while (set_len(all_elements) > 0):
+		elem = set_pop(all_elements)
+		if (string_startswith(read_type(model, elem), "PartialRuntime/")):
+			retype(model, elem, "NewFullRuntime/" + cast_string(list_read(string_split_nr(read_type(model, elem), "/", 1), 1)))
+
+	return True!

+ 76 - 0
models/cbd_partial_runtime.mvc

@@ -0,0 +1,76 @@
+include "primitives.alh"
+
+Class Float {}
+Class String {}
+
+Class Block{
+    signal : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+Class ICBlock{
+    last_in : Float {
+        target_lower_cardinality = 0
+        target_upper_cardinality = 1
+    }
+}
+
+Class ConstantBlock{
+    value : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Class ProbeBlock{
+    name : String {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Class AdditionBlock{}
+Class NegatorBlock{}
+Class MultiplyBlock{}
+Class InverseBlock{}
+Class DelayBlock{}
+Class IntegratorBlock{
+    last_out : Float {
+        target_lower_cardinality = 0
+        target_upper_cardinality = 1
+    }
+}
+
+Class DerivatorBlock{}
+Class Time{
+    lower_cardinality = 1
+    upper_cardinality = 1
+
+    start_time : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+
+    current_time : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Association Link(Block, Block){}
+Association InitialCondition(Block, ICBlock){
+    source_lower_cardinality = 0
+    source_upper_cardinality = 1
+}
+
+Inheritance (ConstantBlock, Block){}
+Inheritance (AdditionBlock, Block){}
+Inheritance (NegatorBlock, Block){}
+Inheritance (MultiplyBlock, Block){}
+Inheritance (InverseBlock, Block){}
+Inheritance (ICBlock, Block){}
+Inheritance (DelayBlock, ICBlock){}
+Inheritance (DerivatorBlock, ICBlock){}
+Inheritance (IntegratorBlock, ICBlock){}
+Inheritance (ProbeBlock, Block){}

+ 12 - 0
models/cbd_restartSim.alc

@@ -0,0 +1,12 @@
+include "primitives.alh"
+include "io.alh"
+include "utils.alh"
+
+Boolean function main(model : Element):
+	Element tasknames
+	Element root
+	root = read_root()
+	tasknames = set_copy(root[get_taskname()]["globals"]["pm_tasks"]["value"]["simulate"])
+	while (set_len(tasknames) > 0):
+		give_input_to_other(set_pop(tasknames), "")
+	return True!

+ 76 - 0
models/cbd_runtime.mvc

@@ -0,0 +1,76 @@
+include "primitives.alh"
+
+Class Float {}
+Class String {}
+
+Class Block{
+    signal : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+Class ICBlock{
+    last_in : Float {
+        target_lower_cardinality = 0
+        target_upper_cardinality = 1
+    }
+}
+
+Class ConstantBlock{
+    value : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Class ProbeBlock{
+    name : String {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Class AdditionBlock{}
+Class NegatorBlock{}
+Class MultiplyBlock{}
+Class InverseBlock{}
+Class DelayBlock{}
+Class IntegratorBlock{
+    last_out : Float {
+        target_lower_cardinality = 0
+        target_upper_cardinality = 1
+    }
+}
+
+Class DerivatorBlock{}
+Class Time{
+    lower_cardinality = 1
+    upper_cardinality = 1
+
+    start_time : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+
+    current_time : Float {
+        target_lower_cardinality = 1
+        target_upper_cardinality = 1
+    }
+}
+
+Association Link(Block, Block){}
+Association InitialCondition(Block, ICBlock){
+    source_lower_cardinality = 0
+    source_upper_cardinality = 1
+}
+
+Inheritance (ConstantBlock, Block){}
+Inheritance (AdditionBlock, Block){}
+Inheritance (NegatorBlock, Block){}
+Inheritance (MultiplyBlock, Block){}
+Inheritance (InverseBlock, Block){}
+Inheritance (ICBlock, Block){}
+Inheritance (DelayBlock, ICBlock){}
+Inheritance (DerivatorBlock, ICBlock){}
+Inheritance (IntegratorBlock, ICBlock){}
+Inheritance (ProbeBlock, Block){}

+ 467 - 0
models/cbd_simulate.alc

@@ -0,0 +1,467 @@
+include "primitives.alh"
+include "modelling.alh"
+include "object_operations.alh"
+include "conformance_scd.alh"
+include "io.alh"
+include "metamodels.alh"
+include "mini_modify.alh"
+
+Boolean function main(model : Element):
+	String cmd
+	Boolean running
+	Element schedule_init
+	Element schedule_run
+	Element schedule
+	Float current_time
+
+	String time
+	time = set_pop(allInstances(model, "FullRuntime/Time"))
+	current_time = read_attribute(model, time, "current_time")
+
+	schedule_init = create_schedule(model)
+	schedule_run = read_root()
+
+	Element nodes
+	Element inputs
+	String node
+	nodes = allInstances(model, "FullRuntime/Block")
+	inputs = dict_create()
+	while (set_len(nodes) > 0):
+		node = set_pop(nodes)
+		dict_add(inputs, node, allAssociationOrigins(model, node, "FullRuntime/Link"))
+
+	while (bool_not(has_input())):
+		if (read_attribute(model, time, "start_time") == read_attribute(model, time, "current_time")):
+			schedule = schedule_init
+		else:
+			if (element_eq(schedule_run, read_root())):
+				schedule_run = create_schedule(model)
+			schedule = schedule_run
+		current_time = step_simulation(model, schedule, current_time, inputs)
+
+	instantiate_attribute(model, time, "current_time", current_time)
+	output("CLOSE")
+	return True!
+
+Element function create_schedule(model : Element):
+	// Create nice graph first
+	Element nodes
+	Element successors
+	Element predecessors
+	String element_name
+	Element incoming_links
+	Element all_blocks
+
+	nodes = allInstances(model, "FullRuntime/Block")
+	successors = dict_create()
+	predecessors = dict_create()
+	while (set_len(nodes) > 0):
+		element_name = set_pop(nodes)
+		if (bool_not(dict_in(successors, element_name))):
+			dict_add(successors, element_name, create_node())
+		if (bool_not(dict_in(predecessors, element_name))):
+			dict_add(predecessors, element_name, create_node())
+
+		if (is_nominal_instance(model, element_name, "FullRuntime/ICBlock")):
+			if (bool_not(is_physical_float(read_attribute(model, element_name, "last_in")))):
+				incoming_links = allIncomingAssociationInstances(model, element_name, "FullRuntime/InitialCondition")
+			else:
+				incoming_links = create_node()
+			if (is_nominal_instance(model, element_name, "FullRuntime/DerivatorBlock")):
+				Element new_incoming_links
+				new_incoming_links = allIncomingAssociationInstances(model, element_name, "FullRuntime/Link")
+				while (read_nr_out(new_incoming_links) > 0):
+					list_append(incoming_links, set_pop(new_incoming_links))
+		else:
+			incoming_links = allIncomingAssociationInstances(model, element_name, "FullRuntime/Link")
+
+		while (set_len(incoming_links) > 0):
+			String source
+			source = readAssociationSource(model, set_pop(incoming_links))
+			if (bool_not(dict_in(successors, source))):
+				dict_add(successors, source, create_node())
+			set_add(successors[source], element_name)
+			set_add(predecessors[element_name], source)
+	
+	Element values
+	values = create_node()
+	dict_add(values, "model", model)
+	dict_add(values, "S", create_node())
+	dict_add(values, "index", 0)
+	dict_add(values, "indices", create_node())
+	dict_add(values, "lowlink", create_node())
+	dict_add(values, "onStack", create_node())
+	dict_add(values, "successors", successors)
+	dict_add(values, "predecessors", predecessors)
+	dict_add(values, "SCC", create_node())
+
+	nodes = get_topolist(values)
+	while (list_len(nodes) > 0):
+		strongconnect(list_pop_final(nodes), values)
+
+	return values["SCC"]!
+
+Element function get_topolist(values : Element):
+	Element result
+	Element predecessors
+	Element remaining
+	String current_element
+	Element cur_predecessors
+
+	result = list_create()
+	predecessors = dict_copy(values["predecessors"])
+
+	while (dict_len(predecessors) > 0):
+		remaining = dict_keys(predecessors)
+		while (set_len(remaining) > 0):
+			current_element = set_pop(remaining)
+			cur_predecessors = predecessors[current_element]
+			if (set_len(set_overlap(list_to_set(result), cur_predecessors)) == set_len(cur_predecessors)):
+				// All predecessors of this node have already been visited
+				dict_delete(predecessors, current_element)
+				remaining = dict_keys(predecessors)
+				list_append(result, current_element)
+
+	return result!
+
+Integer function min(a : Integer, b : Integer):
+	if (a < b):
+		return a!
+	else:
+		return b!
+
+Void function strongconnect(v : String, values : Element):
+	if (dict_in(values["indices"], v)):
+		return!
+
+	dict_overwrite(values["indices"], v, values["index"])
+	dict_overwrite(values["lowlink"], v, values["index"])
+	dict_overwrite(values, "index", cast_integer(values["index"]) + 1)
+
+	list_append(values["S"], v)
+	dict_overwrite(values["onStack"], v, True)
+	
+	Element successors
+	String w
+	successors = values["successors"][v]
+	while (set_len(successors) > 0):
+		w = set_pop(successors)
+		if (bool_not(dict_in(values["indices"], w))):
+			strongconnect(w, values)
+			dict_overwrite(values["lowlink"], v, min(values["lowlink"][v], values["lowlink"][w]))
+		elif (dict_in(values["onStack"], w)):
+			if (values["onStack"][w]):
+				dict_overwrite(values["lowlink"], v, min(values["lowlink"][v], values["indices"][w]))
+	
+	if (value_eq(values["lowlink"][v], values["indices"][v])):
+		Element scc
+		scc = create_node()
+		// It will always differ now
+		w = list_pop_final(values["S"])
+		list_append(scc, w)
+		dict_overwrite(values["onStack"], w, False)
+		while (w != v):
+			w = list_pop_final(values["S"])
+			list_append(scc, w)
+			dict_overwrite(values["onStack"], w, False)
+		list_insert(values["SCC"], scc, 0)
+
+	return!
+
+Boolean function solve_scc(model : Element, scc : Element):
+	Element m
+	Integer i
+	Integer j
+	String block
+	String blocktype
+	Element incoming
+	String selected
+	Float constant
+	Element t
+
+	// Construct the matrix first, with as many rows as there are variables
+	// Number of columns is 1 higher
+	i = 0
+	m = create_node()
+	while (i < read_nr_out(scc)):
+		j = 0
+		t = create_node()
+		while (j < (read_nr_out(scc) + 1)):
+			list_append(t, 0.0)
+			j = j + 1
+		list_append(m, t)
+		i = i + 1
+
+	// Matrix initialized to 0.0
+	i = 0
+	while (i < read_nr_out(scc)):
+		// First element of scc
+		block = scc[i]
+		blocktype = read_type(model, block)
+
+		// First write 1 in the current block
+		dict_overwrite(m[i], i, 1.0)
+
+		// Now check all blocks that are incoming
+		if (blocktype == "FullRuntime/AdditionBlock"):
+			constant = 0.0
+		elif (blocktype == "FullRuntime/MultiplyBlock"):
+			constant = 1.0
+
+		incoming = allIncomingAssociationInstances(model, block, "Link")
+
+		Integer index_to_write_constant
+		index_to_write_constant = -1
+		while (read_nr_out(incoming) > 0):
+			selected = readAssociationSource(model, set_pop(incoming))
+
+			if (set_in(scc, selected)):
+				// Part of the loop, so in the index of selected in scc
+				// Five options:
+				if (blocktype == "FullRuntime/AdditionBlock"):
+					// 1) AdditionBlock
+					// Add the negative of this signal, which is as of yet unknown
+					// x = y + z --> x - y - z = 0
+					dict_overwrite(m[i], list_index_of(scc, selected), -1.0)
+				elif (blocktype == "FullRuntime/MultiplyBlock"):
+					// 2) MultiplyBlock
+					if (index_to_write_constant != -1):
+						return False!
+					index_to_write_constant = list_index_of(scc, selected)
+				elif (blocktype == "FullRuntime/NegatorBlock"):
+					// 3) NegatorBlock
+					// Add the positive of the signal, which is as of yet unknown
+					dict_overwrite(m[i], list_index_of(scc, selected), 1.0)
+				elif (blocktype == "FullRuntime/DelayBlock"):
+					// 5) DelayBlock
+					// Just copies a single value
+					dict_overwrite(m[i], list_index_of(scc, selected), -1.0)
+				else:
+					// Block that cannot be handled
+					return False!
+			else:
+				// A constant, which we can assume is already computed and thus usable
+				if (blocktype == "FullRuntime/AdditionBlock"):
+					constant = constant + cast_float(read_attribute(model, selected, "signal"))
+					dict_overwrite(m[i], read_nr_out(scc), constant)
+				elif (blocktype == "FullRuntime/MultiplyBlock"):
+					constant = constant * cast_float(read_attribute(model, selected, "signal"))
+					// Not written to constant part, as multiplies a variable
+
+				// Any other block is impossible:
+				// * Constant would never be part of a SCC
+				// * Delay would never get an incoming constant
+				// * Negation and Inverse only get 1 input, which is a variable in a loop
+				// * Integrator and Derivator never get an incoming constant
+
+		if (index_to_write_constant != -1):
+			dict_overwrite(m[i], index_to_write_constant, -constant)
+
+		i = i + 1
+
+	// Constructed a complete matrix, so we can start!
+	log(matrix2string(m))
+
+	// Solve matrix now
+	eliminateGaussJordan(m)
+
+	// Now go over m and set signals for each element
+	// Assume that everything worked out...
+	i = 0
+	while (i < read_nr_out(m)):
+		block = scc[i]
+		instantiate_attribute(model, block, "signal", m[i][read_nr_out(scc)])
+		log((("Solved " + block) + " to ") + cast_string(m[i][read_nr_out(scc)]))
+		i = i + 1
+
+	return True!
+
+Integer function list_index_of(lst : Element, elem : Element):
+	Integer i
+	i = 0
+	while (i < read_nr_out(lst)):
+		if (value_eq(list_read(lst, i), elem)):
+			return i!
+		else:
+			i = i + 1
+	return -1!
+
+Float function step_simulation(model : Element, schedule : Element, time : Float, inputs : Element):
+	Float signal
+	Element incoming
+	String selected
+	String block
+	String elem
+	String blocktype
+	Element memory_blocks
+	Integer i
+	Float delta_t
+	Element scc
+
+	delta_t = 0.1
+
+	memory_blocks = set_create()
+	i = 0
+	while (i < list_len(schedule)):
+		scc = list_read(schedule, i)
+		i = i + 1
+
+		if (list_len(scc) > 1):
+			if (bool_not(solve_scc(model, scc))):
+				output("ALGEBRAIC_LOOP")
+				return time!
+		else:
+			block = list_read(scc, 0)
+
+			// Execute "block"
+			blocktype = read_type(model, block)
+			incoming = set_copy(inputs[block])
+			if (blocktype == "FullRuntime/ConstantBlock"):
+				signal = read_attribute(model, block, "value")
+			elif (blocktype == "FullRuntime/AdditionBlock"):
+				signal = 0.0
+				while (set_len(incoming) > 0):
+					selected = set_pop(incoming)
+					signal = signal + cast_float(read_attribute(model, selected, "signal"))
+			elif (blocktype == "FullRuntime/MultiplyBlock"):
+				signal = 1.0
+				while (set_len(incoming) > 0):
+					selected = set_pop(incoming)
+					signal = signal * cast_float(read_attribute(model, selected, "signal"))
+			elif (blocktype == "FullRuntime/NegatorBlock"):
+				signal = 0.0
+				while (set_len(incoming) > 0):
+					selected = set_pop(incoming)
+					signal = float_neg(cast_float(read_attribute(model, selected, "signal")))
+			elif (blocktype == "FullRuntime/InverseBlock"):
+				signal = 0.0
+				while (set_len(incoming) > 0):
+					selected = set_pop(incoming)
+					signal = float_division(1.0, cast_float(read_attribute(model, selected, "signal")))
+			elif (blocktype == "FullRuntime/DelayBlock"):
+				signal = 0.0
+				if (bool_not(is_physical_float(read_attribute(model, block, "last_in")))):
+					// No memory yet, so use initial condition
+					incoming = allAssociationOrigins(model, block, "FullRuntime/InitialCondition")
+					while (set_len(incoming) > 0):
+						selected = set_pop(incoming)
+						signal = cast_float(read_attribute(model, selected, "signal"))
+				else:
+					signal = read_attribute(model, block, "last_in")
+				set_add(memory_blocks, block)
+			elif (blocktype == "FullRuntime/IntegratorBlock"):
+				if (bool_not(is_physical_float(read_attribute(model, block, "last_in")))):
+					// No history yet, so use initial values
+					incoming = allAssociationOrigins(model, block, "FullRuntime/InitialCondition")
+					while (set_len(incoming) > 0):
+						selected = set_pop(incoming)
+						signal = cast_float(read_attribute(model, selected, "signal"))
+				else:
+					signal = cast_float(read_attribute(model, block, "last_out")) + (delta_t * cast_float(read_attribute(model, block, "last_in")))
+				instantiate_attribute(model, block, "last_out", signal)
+				set_add(memory_blocks, block)
+			elif (blocktype == "FullRuntime/DerivatorBlock"):
+				if (bool_not(is_physical_float(read_attribute(model, block, "last_in")))):
+					// No history yet, so use initial values
+					incoming = allAssociationOrigins(model, block, "FullRuntime/InitialCondition")
+					while (set_len(incoming) > 0):
+						selected = set_pop(incoming)
+						signal = cast_float(read_attribute(model, selected, "signal"))
+				else:
+					while (set_len(incoming) > 0):
+						selected = set_pop(incoming)
+						signal = (cast_float(read_attribute(model, selected, "signal")) - cast_float(read_attribute(model, block, "last_in"))) / delta_t
+				set_add(memory_blocks, block)
+			elif (blocktype == "FullRuntime/ProbeBlock"):
+				while (set_len(incoming) > 0):
+					signal = cast_float(read_attribute(model, set_pop(incoming), "signal"))
+					output(cast_string(time) + " " + cast_string(read_attribute(model, block, "name")) + " " + cast_string(signal))
+
+			instantiate_attribute(model, block, "signal", signal)
+	
+	while (set_len(memory_blocks) > 0):
+		block = set_pop(memory_blocks)
+		// Update memory
+		incoming = set_copy(inputs[block])
+		while (set_len(incoming) > 0):
+			selected = set_pop(incoming)
+			instantiate_attribute(model, block, "last_in", cast_float(read_attribute(model, selected, "signal")))
+
+	// Increase simulation time
+	return time + delta_t!
+
+Void function eliminateGaussJordan(m : Element):
+	Integer i
+	Integer j
+	Integer f
+	Integer g
+	Boolean searching
+	Element t
+	Float divisor
+
+	i = 0
+	j = 0
+
+	while (i < read_nr_out(m)):
+		// Make sure pivot m[i][j] != 0, swapping if necessary
+		while (cast_float(m[i][j]) == 0.0):
+			// Is zero, so find row which is not zero
+			f = i + 1
+			searching = True
+			while (searching):
+				if (f >= read_nr_out(m)):
+					// No longer any rows left, so just increase column counter
+					searching = False
+					j = j + 1
+				else:
+					if (cast_float(m[f][j]) == 0.0):
+						// Also zero, so continue
+						f = f + 1
+					else:
+						// Found non-zero, so swap row
+						t = cast_float(m[f])
+						dict_overwrite(m, f, cast_float(m[i]))
+						dict_overwrite(m, i, t)
+						searching = False
+			// If we have increased j, we will just start the loop again (possibly), as m[i][j] might be zero again
+
+		// Pivot in m[i][j] guaranteed to not be 0
+		// Now divide complete row by value of m[i][j] to make it equal 1
+		f = j
+		divisor = cast_float(m[i][j])
+		while (f < read_nr_out(m[i])):
+			dict_overwrite(m[i], f, float_division(cast_float(m[i][f]), divisor))
+			f = f + 1
+
+		// Eliminate all rows in the j-th column, except the i-th row
+		f = 0
+		while (f < read_nr_out(m)):
+			if (bool_not(f == i)):
+				g = j
+				divisor = cast_float(m[f][j])
+				while (g < read_nr_out(m[f])):
+					dict_overwrite(m[f], g, cast_float(m[f][g]) - (divisor * cast_float(m[i][g])))
+					g = g + 1
+			f = f + 1
+
+		// Increase row and column
+		i = i + 1
+		j = j + 1
+
+	return !
+
+String function matrix2string(m : Element):
+	Integer i
+	Integer j
+	String result
+
+	result = ""
+	i = 0
+	while (i < read_nr_out(m)):
+		j = 0
+		while (j < read_nr_out(m[i])):
+			result = result + cast_string(m[i][j]) + ", "
+			j = j + 1
+		i = i + 1
+		result = result + "\n"
+	return result!

+ 54 - 0
models/cbd_spring.mvc

@@ -0,0 +1,54 @@
+ConstantBlock cte_k {
+    value = 1
+}
+
+ConstantBlock cte_g {
+    value = 10
+}
+
+ConstantBlock cte_m {
+    value = 1
+}
+
+ConstantBlock cte_v0 {
+    value = 1
+}
+
+ConstantBlock cte_y0 {
+    value = 20
+}
+
+MultiplyBlock m0 {}
+MultiplyBlock m1 {}
+MultiplyBlock m2 {}
+NegatorBlock n0 {}
+InverseBlock i0 {}
+AdditionBlock a0 {}
+IntegratorBlock int0 {}
+IntegratorBlock int1 {}
+
+ProbeBlock pv {
+    name = "velocity"
+}
+ProbeBlock py {
+    name = "displacement"
+}
+
+Link (cte_k, m0) {}
+Link (int1, m0) {}
+Link (cte_g, m1) {}
+Link (cte_m, m1) {}
+Link (cte_m, i0) {}
+Link (m0, n0) {}
+Link (n0, a0) {}
+Link (m1, a0) {}
+Link (i0, m2) {}
+Link (a0, m2) {}
+Link (m2, int0) {}
+Link (int0, int1) {}
+
+InitialCondition (cte_v0, int0) {}
+InitialCondition (cte_y0, int1) {}
+
+Link (int0, pv) {}
+Link (int1, py) {}

+ 69 - 0
models/cbd_toRuntime.alc

@@ -0,0 +1,69 @@
+include "primitives.alh"
+include "modelling.alh"
+include "object_operations.alh"
+
+String function map_D2P(model : Element, name : String):
+	Element destinations
+	String pick
+
+	destinations = allAssociationDestinations(model, name, "D2P_block")
+
+	pick = name
+	while (pick == name):
+		pick = set_pop(destinations)
+
+	return pick!
+
+Boolean function main(model : Element):
+	Element all_blocks
+	String element_name
+	String new_element_name
+	String mm_type_name
+	Element all_links
+
+	all_blocks = allInstances(model, "Design/Block")
+	while (set_len(all_blocks) > 0):
+		element_name = set_pop(all_blocks)
+		mm_type_name = "PartialRuntime/" + cast_string(list_read(string_split(read_type(model, element_name), "/"), 1))
+
+		if (set_len(allOutgoingAssociationInstances(model, element_name, "D2P_block")) == 0):
+			// New design element, so create in partial runtime model as well
+			new_element_name = instantiate_node(model, mm_type_name, "")
+			instantiate_link(model, "D2P_block", "", element_name, new_element_name)
+
+		// Always update the value of attributes of PartialRuntime
+		new_element_name = map_D2P(model, element_name)
+		if (mm_type_name == "PartialRuntime/ConstantBlock"):
+            instantiate_attribute(model, new_element_name, "value", read_attribute(model, element_name, "value"))
+		elif (mm_type_name == "PartialRuntime/ProbeBlock"):
+            instantiate_attribute(model, new_element_name, "name", read_attribute(model, element_name, "name"))
+
+	all_blocks = allInstances(model, "PartialRuntime/Block")
+	while (set_len(all_blocks) > 0):
+		element_name = set_pop(all_blocks)
+		if (set_len(allIncomingAssociationInstances(model, element_name, "D2P_block")) == 0):
+			// Old partial runtime element, so remove
+			model_delete_element(model, element_name)
+
+	// Delete all existing links
+	all_links = allInstances(model, "PartialRuntime/Link")
+	while (set_len(all_links) > 0):
+		model_delete_element(model, set_pop(all_links))
+
+	all_links = allInstances(model, "PartialRuntime/InitialCondition")
+	while (set_len(all_links) > 0):
+		model_delete_element(model, set_pop(all_links))
+
+	// Recreate all of them
+    all_links = allInstances(model, "Design/Link")
+    while (set_len(all_links) > 0):
+        element_name = set_pop(all_links)
+        instantiate_link(model, "PartialRuntime/Link", "", map_D2P(model, readAssociationSource(model, element_name)), map_D2P(model, readAssociationDestination(model, element_name)))
+
+    all_links = allInstances(model, "Design/InitialCondition")
+    while (set_len(all_links) > 0):
+        element_name = set_pop(all_links)
+        instantiate_link(model, "PartialRuntime/InitialCondition", "", map_D2P(model, readAssociationSource(model, element_name)), map_D2P(model, readAssociationDestination(model, element_name)))
+
+
+	return True!

+ 143 - 0
models/pm_live_CBD.mvc

@@ -0,0 +1,143 @@
+Start start {}
+
+Exec toRuntime_0 {
+    name = "models/CBD/toRuntime"
+}
+
+Exec merge_0 {
+    name = "models/CBD/merge"
+}
+
+Exec edit {
+    name = "models/CBD/edit"
+}
+
+Exec toRuntime {
+    name = "models/CBD/toRuntime"
+}
+
+Exec restartSim {
+    name = "models/CBD/restartSim"
+}
+
+Exec simulate {
+    name = "models/CBD/simulate"
+}
+
+Exec merge {
+    name = "models/CBD/merge"
+}
+
+Fork fork1 {}
+Fork fork2 {}
+
+Data traceability_D2P {
+    name = "traceability_D2P"
+    type = "formalisms/Tracability"
+}
+Data traceability_P2F {
+    name = "traceability_P2F"
+    type = "formalisms/Tracability"
+}
+
+Data design_model {
+    name = "design_model"
+    type = "formalisms/CBD/Design_MM"
+}
+
+Data partial_runtime_model {
+    name = "partial_runtime_model"
+    type = "formalisms/CBD/PartialRuntime_MM"
+}
+
+Data full_runtime_model {
+    name = "full_runtime_model"
+    type = "formalisms/CBD/FullRuntime_MM"
+}
+
+Next (start, toRuntime_0) {}
+Next (toRuntime_0, merge_0) {}
+Next (merge_0, fork1) {}
+Next (fork1, edit) {}
+Next (fork1, simulate) {}
+Next (edit, toRuntime) {}
+Next (toRuntime, fork2) {}
+Next (fork2, edit) {}
+Next (fork2, restartSim) {}
+Next (simulate, merge) {}
+Next (merge, simulate) {}
+
+Consumes (edit, design_model) {
+    name = "Design"
+}
+Produces (edit, design_model) {
+    name = "Design"
+}
+
+Consumes (toRuntime, design_model) {
+    name = "Design"
+}
+Consumes (toRuntime, partial_runtime_model) {
+    name = "PartialRuntime"
+}
+Consumes (toRuntime, traceability_D2P) {
+    name = "__traceability"
+}
+Produces (toRuntime, traceability_D2P) {
+    name = "__traceability"
+}
+Produces (toRuntime, partial_runtime_model) {
+    name = "PartialRuntime"
+}
+
+Consumes (toRuntime_0, design_model) {
+    name = "Design"
+}
+Consumes (toRuntime_0, traceability_D2P) {
+    name = "__traceability"
+}
+Produces (toRuntime_0, traceability_D2P) {
+    name = "__traceability"
+}
+Produces (toRuntime_0, partial_runtime_model) {
+    name = "PartialRuntime"
+}
+
+Consumes (merge, partial_runtime_model) {
+    name = "PartialRuntime"
+}
+Consumes (merge, full_runtime_model) {
+    name = "FullRuntime"
+}
+Consumes (merge, traceability_P2F) {
+    name = "__traceability"
+}
+Produces (merge, traceability_P2F) {
+    name = "__traceability"
+}
+Produces (merge, full_runtime_model) {
+    name = "NewFullRuntime"
+}
+
+Consumes (merge_0, partial_runtime_model) {
+    name = "PartialRuntime"
+}
+Consumes (merge_0, full_runtime_model) {
+    name = "FullRuntime"
+}
+Consumes (merge_0, traceability_P2F) {
+    name = "__traceability"
+}
+Produces (merge_0, traceability_P2F) {
+    name = "__traceability"
+}
+Produces (merge_0, full_runtime_model) {
+    name = "NewFullRuntime"
+}
+
+Consumes (simulate, full_runtime_model) {
+    name = "FullRuntime"
+}
+Produces (simulate, full_runtime_model) {
+    name = "FullRuntime"
+}

+ 214 - 0
models/render_OD.alc

@@ -0,0 +1,214 @@
+include "primitives.alh"
+include "modelling.alh"
+include "object_operations.alh"
+include "utils.alh"
+
+Boolean function main(model : Element):
+	Element elements
+	String class
+	Element attrs
+	Element attr_keys
+	String attr_key
+	String group
+	String elem
+	Integer loc_x
+	Integer loc_y
+	Integer text_loc
+	loc_x = 10
+	loc_y = 10
+
+	Element to_remove
+	String elem_to_remove
+	Element groups
+	Element class_types
+	Element metamodel
+	metamodel = model["metamodel"]
+	String class_type
+
+	// Construct our own kind of tracability
+	Element cs_to_as
+	Element as_to_cs
+	String asid
+	cs_to_as = dict_create()
+	as_to_cs = dict_create()
+	groups = allInstances(model, "rendered/Group")
+	while (set_len(groups) > 0):
+		group = set_pop(groups)
+		asid = read_attribute(model, group, "__asid")
+		dict_add(cs_to_as, group, "abstract/" + asid)
+		dict_add(as_to_cs, "abstract/" + asid, group)
+
+	// Now render everything
+	groups = dict_create()
+	class_types = allInstances(metamodel, "Class")
+	while (set_len(class_types) > 0):
+		class_type = set_pop(class_types)
+
+		if (string_startswith(class_type, "abstract/")):
+			elements = allInstances(model, class_type)
+
+			while (set_len(elements) > 0):
+				class = set_pop(elements)
+
+				if (is_edge(model["model"][class])):
+					continue!
+				
+				Integer x
+				Integer y
+				x = loc_x
+				y = loc_y
+
+				// Check if there is already an associated element
+				if (dict_in(as_to_cs, class)):
+					// Yes, but is it still clean?
+					Element related_groups
+					group = as_to_cs[class]
+
+					if (bool_not(read_attribute(model, group, "dirty"))):
+						dict_add(groups, class, group)
+						continue!
+					else:
+						group = as_to_cs[class]
+						to_remove = allAssociationDestinations(model, group, "rendered/contains")
+						x = create_value(read_attribute(model, group, "x"))
+						y = create_value(read_attribute(model, group, "y"))
+
+						while (set_len(to_remove) > 0):
+							elem_to_remove = set_pop(to_remove)
+							if (read_type(model, elem_to_remove) == "rendered/Group"):
+								set_add(to_remove, elem_to_remove)
+							else:
+								model_delete_element(model, elem_to_remove)
+						model_delete_element(model, group)
+						dict_delete(as_to_cs, class)
+
+				if (dict_in(groups, class)):
+					// Already rendered this, so skip
+					continue!
+
+				text_loc = 5
+
+				group = instantiate_node(model, "rendered/Group", "")
+				instantiate_attribute(model, group, "x", x)
+				instantiate_attribute(model, group, "y", y)
+				instantiate_attribute(model, group, "__asid", list_read(string_split_nr(class, "/", 1), 1))
+				instantiate_attribute(model, group, "layer", 0)
+				dict_add(groups, class, group)
+
+				loc_x = loc_x + 250
+				if (loc_x > 2000):
+					loc_x = 10
+					loc_y = loc_y + 300
+
+				elem = instantiate_node(model, "rendered/Rectangle", "")
+				instantiate_attribute(model, elem, "x", 0)
+				instantiate_attribute(model, elem, "y", 0)
+				instantiate_attribute(model, elem, "height", 40 + set_len(getAttributes(model, class)) * 20)
+				instantiate_attribute(model, elem, "width", 200)
+				instantiate_attribute(model, elem, "lineWidth", 2) 
+				instantiate_attribute(model, elem, "lineColour", "black")
+				instantiate_attribute(model, elem, "fillColour", "white")
+				instantiate_attribute(model, elem, "layer", 1)
+				instantiate_link(model, "rendered/contains", "", group, elem)
+
+				elem = instantiate_node(model, "rendered/Text", "")
+				instantiate_attribute(model, elem, "x", 5)
+				instantiate_attribute(model, elem, "y", 3)
+				instantiate_attribute(model, elem, "lineWidth", 1)
+				instantiate_attribute(model, elem, "lineColour", "black")
+				instantiate_attribute(model, elem, "text", string_join(cast_value(list_read(string_split_nr(class, "/", 1), 1)), " : " + cast_value(list_read(string_split_nr(read_type(model, class), "/", 1), 1))))
+				instantiate_attribute(model, elem, "layer", 2)
+				instantiate_link(model, "rendered/contains", "", group, elem)
+
+				elem = instantiate_node(model, "rendered/Line", "")
+				instantiate_attribute(model, elem, "x", 0)
+				instantiate_attribute(model, elem, "y", 20)
+				instantiate_attribute(model, elem, "targetX", 200)
+				instantiate_attribute(model, elem, "targetY", 20)
+				instantiate_attribute(model, elem, "lineWidth", 1)
+				instantiate_attribute(model, elem, "lineColour", "black")
+				instantiate_attribute(model, elem, "arrow", False)
+				instantiate_attribute(model, elem, "layer", 2)
+				instantiate_link(model, "rendered/contains", "", group, elem)
+
+				attrs = getAttributes(model, class)
+				attr_keys = dict_keys(attrs)
+				while (dict_len(attr_keys) > 0):
+					attr_key = set_pop(attr_keys)
+					elem = instantiate_node(model, "rendered/Text", "")
+					instantiate_attribute(model, elem, "x", 5)
+					instantiate_attribute(model, elem, "y", text_loc + 20)
+					instantiate_attribute(model, elem, "lineWidth", 1)
+					instantiate_attribute(model, elem, "lineColour", "black")
+					instantiate_attribute(model, elem, "text", (attr_key + " = ") + cast_value(attrs[attr_key]))
+					instantiate_attribute(model, elem, "layer", 2)
+					instantiate_link(model, "rendered/contains", "", group, elem)
+					text_loc = text_loc + 15
+
+	// Flush all associations
+	elements = allInstances(model, "rendered/ConnectingLine")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+		model_delete_element(model, class)
+
+	// Rerender associations
+	Element to_render
+	to_render = set_create()
+	class_types = allInstances(metamodel, "Association")
+	while (set_len(class_types) > 0):
+		class_type = set_pop(class_types)
+		log("Checking type " + class_type)
+
+		if (string_startswith(class_type, "abstract/")):
+			elements = allInstances(model, class_type)
+			log("    Checking instance " + class)
+			while (set_len(elements) > 0):
+				class = set_pop(elements)
+				if (is_edge(model["model"][class])):
+					if (bool_not(set_in(to_render, class))):
+						set_add(to_render, class)
+						log("Added!")
+
+	to_render = set_to_list(to_render)
+	Element delayed_elements
+	Integer num_to_render
+	delayed_elements = list_create()
+	while (list_len(to_render) > 0):
+		num_to_render = list_len(to_render)
+		while (list_len(to_render) > 0):
+			class = list_pop_final(to_render)
+			attr_keys = dict_keys(getAttributes(model, class))
+
+			if (bool_not(bool_and(dict_in(groups, readAssociationSource(model, class)), dict_in(groups, readAssociationDestination(model, class))))):
+				list_append(delayed_elements, class)
+				continue!
+
+			elem = instantiate_link(model, "rendered/ConnectingLine", "", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])
+			dict_add(groups, class, elem)
+			if (is_edge(model["model"][readAssociationSource(model, class)])):
+				instantiate_attribute(model, elem, "offsetSourceX", 0)
+				instantiate_attribute(model, elem, "offsetSourceY", 0)
+			else:
+				instantiate_attribute(model, elem, "offsetSourceX", 100)
+				instantiate_attribute(model, elem, "offsetSourceY", 30)
+			if (is_edge(model["model"][readAssociationDestination(model, class)])):
+				instantiate_attribute(model, elem, "offsetTargetX", 0)
+				instantiate_attribute(model, elem, "offsetTargetY", 0)
+			else:
+				instantiate_attribute(model, elem, "offsetTargetX", 100)
+				instantiate_attribute(model, elem, "offsetTargetY", 30)
+			instantiate_attribute(model, elem, "lineWidth", 3)
+			instantiate_attribute(model, elem, "lineColour", "black")
+			instantiate_attribute(model, elem, "arrow", True)
+			instantiate_attribute(model, elem, "__asid", list_read(string_split_nr(class, "/", 1), 1))
+			instantiate_attribute(model, elem, "layer", 0)
+			instantiate_link(model, "rendered/contains", "", group, elem)
+
+		if (num_to_render == list_len(delayed_elements)):
+			log("Could not decrease number of rendered elements anymore... Giving up!")
+			return True!
+		else:
+			to_render = delayed_elements
+			delayed_elements = list_create()
+
+	return True!

+ 184 - 0
models/render_SCD.alc

@@ -0,0 +1,184 @@
+include "primitives.alh"
+include "modelling.alh"
+include "object_operations.alh"
+include "utils.alh"
+
+Boolean function main(model : Element):
+	Element elements
+	String class
+	Element attrs
+	Element attr_keys
+	String attr_key
+	String group
+	String elem
+	Integer loc
+	Integer text_loc
+	Element related_groups
+	loc = 10
+
+	Element groups
+	groups = dict_create()
+
+	elements = allInstances(model, "rendered/Group")
+	while (set_len(elements) > 0):
+		group = set_pop(elements)
+		if (set_len(allIncomingAssociationInstances(model, group, "TracabilityClass")) == 0):
+			Element to_remove
+			String elem_to_remove
+			to_remove = allAssociationDestinations(model, group, "rendered/contains")
+			while (set_len(to_remove) > 0):
+				elem_to_remove = set_pop(to_remove)
+				if (read_type(model, elem_to_remove) == "rendered/Group"):
+					set_add(to_remove, elem_to_remove)
+				else:
+					model_delete_element(model, elem_to_remove)
+			model_delete_element(model, group)
+
+	elements = allInstances(model, "abstract/Class")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+		
+		Integer x
+		Integer y
+		x = loc
+		y = 10
+
+		// Check if there is already an associated element
+		if (set_len(allOutgoingAssociationInstances(model, class, "TracabilityClass")) > 0):
+			// Yes, but is it still clean?
+			Boolean dirty
+			dirty = False
+
+			related_groups = allAssociationDestinations(model, class, "TracabilityClass")
+			while (set_len(related_groups) > 0):
+				group = set_pop(related_groups)
+				if (value_eq(read_attribute(model, group, "dirty"), True)):
+					// No, so mark all as dirty
+					dirty = True
+					break!
+				else:
+					// Yes, so just ignore this!
+					continue!
+
+			if (bool_not(dirty)):
+				dict_add(groups, class, group)
+				continue!
+			else:
+				related_groups = allAssociationDestinations(model, class, "TracabilityClass")
+				Element to_remove
+				String elem_to_remove
+				while (set_len(related_groups) > 0):
+					group = set_pop(related_groups)
+					to_remove = allAssociationDestinations(model, group, "rendered/contains")
+					x = create_value(read_attribute(model, group, "x"))
+					y = create_value(read_attribute(model, group, "y"))
+					while (set_len(to_remove) > 0):
+						elem_to_remove = set_pop(to_remove)
+						if (read_type(model, elem_to_remove) == "rendered/Group"):
+							set_add(to_remove, elem_to_remove)
+						else:
+							model_delete_element(model, elem_to_remove)
+					model_delete_element(model, group)
+
+		attr_keys = dict_keys(getAttributeList(model, class))
+		text_loc = 5
+
+		group = instantiate_node(model, "rendered/Group", "")
+		instantiate_attribute(model, group, "x", x)
+		instantiate_attribute(model, group, "y", y)
+		instantiate_attribute(model, group, "__asid", list_read(string_split_nr(class, "/", 1), 1))
+		instantiate_attribute(model, group, "layer", 0)
+		dict_add(groups, class, group)
+		loc = loc + 200
+
+		elem = instantiate_node(model, "rendered/Rectangle", "")
+		instantiate_attribute(model, elem, "x", 0)
+		instantiate_attribute(model, elem, "y", 0)
+		instantiate_attribute(model, elem, "height", 40 + set_len(getInstantiatableAttributes(model, class, "abstract/AttributeLink")) * 20)
+		instantiate_attribute(model, elem, "width", 150)
+		instantiate_attribute(model, elem, "lineWidth", 2) 
+		instantiate_attribute(model, elem, "lineColour", "black")
+		instantiate_attribute(model, elem, "fillColour", "white")
+		instantiate_attribute(model, elem, "layer", 1)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+		String multiplicities
+		String lower_card
+		String upper_card
+		if (element_eq(read_attribute(model, class, "lower_cardinality"), read_root())):
+			lower_card = "*"
+		else:
+			lower_card = cast_value(read_attribute(model, class, "lower_cardinality"))
+		if (element_eq(read_attribute(model, class, "upper_cardinality"), read_root())):
+			upper_card = "*"
+		else:
+			upper_card = cast_value(read_attribute(model, class, "upper_cardinality"))
+		multiplicities = ((("[" + lower_card) + "..") + upper_card) + "]"
+
+		elem = instantiate_node(model, "rendered/Text", "")
+		instantiate_attribute(model, elem, "x", 5)
+		instantiate_attribute(model, elem, "y", 3)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "black")
+		if (element_neq(read_attribute(model, class, "name"), read_root())):
+			instantiate_attribute(model, elem, "text", string_join(read_attribute(model, class, "name"), "  " + multiplicities))
+		else:
+			instantiate_attribute(model, elem, "text", "(unnamed) " + multiplicities)
+		instantiate_attribute(model, elem, "layer", 2)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+		elem = instantiate_node(model, "rendered/Line", "")
+		instantiate_attribute(model, elem, "x", 0)
+		instantiate_attribute(model, elem, "y", 20)
+		instantiate_attribute(model, elem, "targetX", 150)
+		instantiate_attribute(model, elem, "targetY", 20)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "black")
+		instantiate_attribute(model, elem, "arrow", False)
+		instantiate_attribute(model, elem, "layer", 2)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+		attrs = getInstantiatableAttributes(model, class, "abstract/AttributeLink")
+		attr_keys = dict_keys(attrs)
+		while (dict_len(attr_keys) > 0):
+			attr_key = set_pop(attr_keys)
+			elem = instantiate_node(model, "rendered/Text", "")
+			instantiate_attribute(model, elem, "x", 5)
+			instantiate_attribute(model, elem, "y", text_loc + 20)
+			instantiate_attribute(model, elem, "lineWidth", 1)
+			instantiate_attribute(model, elem, "lineColour", "black")
+			instantiate_attribute(model, elem, "text", (attr_key + " : ") + cast_string(list_read(string_split_nr(attrs[attr_key], "/", 1), 1)))
+			instantiate_attribute(model, elem, "layer", 2)
+			instantiate_link(model, "rendered/contains", "", group, elem)
+			text_loc = text_loc + 15
+
+		instantiate_link(model, "TracabilityClass", "", class, group)
+
+	// Flush all associations
+	elements = allInstances(model, "rendered/ConnectingLine")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+		model_delete_element(model, class)
+
+	// Rerender associations
+	elements = allInstances(model, "abstract/Association")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+
+		attr_keys = dict_keys(getAttributeList(model, class))
+
+		elem = instantiate_link(model, "rendered/ConnectingLine", "", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])
+		instantiate_attribute(model, elem, "offsetSourceX", 75)
+		instantiate_attribute(model, elem, "offsetSourceY", 30)
+		instantiate_attribute(model, elem, "offsetTargetX", 75)
+		instantiate_attribute(model, elem, "offsetTargetY", 30)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "black")
+		instantiate_attribute(model, elem, "arrow", True)
+		instantiate_attribute(model, elem, "__asid", list_read(string_split_nr(class, "/", 1), 1))
+		instantiate_attribute(model, elem, "layer", 0)
+		log("Real ASID: " + cast_value(class))
+		log("Found ASID " + cast_value(list_read(string_split_nr(class, "/", 1), 1)))
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+	return True!

+ 86 - 20
models/upload_models.py

@@ -5,55 +5,121 @@ from modelverse import *
 init()
 login("admin", "admin")
 
+"""
+model_add("formalisms/Trace", "formalisms/SimpleClassDiagrams", open("models/trace.mvc", 'r').read())
+model_add("models/trace", "formalisms/Trace", open("models/trace_example.mvc", 'r').read())
+
+model_add("formalisms/MM_render/plot", "formalisms/SimpleClassDiagrams", open("models/MM_rendered_plot.mvc", 'r').read())
+def add_trace_trace(model):
+    instantiate(model, "Association", ("abstract/Signal", "rendered/Dataset"), ID="TracabilityLink")
+
+transformation_add_MT({"abstract": "formalisms/Trace", "rendered": "formalisms/MM_render/plot"}, {"rendered": "formalisms/MM_render/plot"}, "models/trace_mapper", open("models/trace_mapper.mvc", 'r').read(), add_trace_trace)
+"""
+
+model_add("formalisms/MM_render/graphical", "formalisms/SimpleClassDiagrams", open("models/MM_render.mvc", 'r').read())
+
+def scd_tracability(model):
+    instantiate(model, "Association", ("abstract/Class", "rendered/Group"), ID="TracabilityClass")
+    instantiate(model, "Association", ("abstract/Association", "rendered/ConnectingLine"), ID="TracabilityAssociation")
+
+transformation_add_AL({"rendered": "formalisms/MM_render/graphical", "abstract": "formalisms/SimpleClassDiagrams"}, {"rendered": "formalisms/MM_render/graphical"}, "models/render_SCD", open("models/render_SCD.alc", 'r').read(), callback=scd_tracability)
+
+model_add("formalisms/CBD/Design_MM", "formalisms/SimpleClassDiagrams", open("models/cbd_design.mvc", 'r').read())
+model_add("formalisms/CBD/PartialRuntime_MM", "formalisms/SimpleClassDiagrams", open("models/cbd_partial_runtime.mvc", 'r').read())
+model_add("formalisms/CBD/FullRuntime_MM", "formalisms/SimpleClassDiagrams", open("models/cbd_runtime.mvc", 'r').read())
+
+model_add("models/CBD_spring", "formalisms/CBD/Design_MM", open("models/cbd_spring.mvc", 'r').read())
+
+transformation_add_MANUAL({"Design": "formalisms/CBD/Design_MM"}, {"Design": "formalisms/CBD/Design_MM"}, "models/CBD/edit")
+
+def trace_D2P(model):
+    instantiate(model, "Association", ("Design/Block", "PartialRuntime/Block"), ID="D2P_block")
+
+transformation_add_AL({"Design": "formalisms/CBD/Design_MM", "PartialRuntime": "formalisms/CBD/PartialRuntime_MM"}, {"PartialRuntime": "formalisms/CBD/PartialRuntime_MM"}, "models/CBD/toRuntime", open("models/cbd_toRuntime.alc", 'r').read(), trace_D2P)
+
+def trace_P2F(model):
+    instantiate(model, "Association", ("PartialRuntime/Block", "FullRuntime/Block"), ID="P2F_block")
+
+transformation_add_AL({"PartialRuntime": "formalisms/CBD/PartialRuntime_MM", "FullRuntime": "formalisms/CBD/FullRuntime_MM"}, {"NewFullRuntime": "formalisms/CBD/FullRuntime_MM"}, "models/CBD/merge", open("models/cbd_merge.alc", 'r').read(), trace_P2F)
+transformation_add_AL({"FullRuntime": "formalisms/CBD/FullRuntime_MM"}, {"FullRuntime": "formalisms/CBD/FullRuntime_MM"}, "models/CBD/simulate", open("models/cbd_simulate.alc", 'r').read())
+transformation_add_AL({}, {}, "models/CBD/restartSim", open("models/cbd_restartSim.alc", 'r').read())
+
+model_add("models/live_modelling_CBD", "formalisms/ProcessModel", open("models/pm_live_CBD.mvc", 'r').read())
+
+def add_trace_CBD(model):
+    instantiate(model, "Association", ("abstract/Block", "rendered/Group"), ID="TracabilityClass")
+
+transformation_add_AL({"abstract": "formalisms/CBD/Design_MM", "rendered": "formalisms/MM_render/graphical"}, {"rendered": "formalisms/MM_render/graphical"}, "models/CBD/render_graphical", open("models/CBD_mapper.alc", 'r').read(), add_trace_CBD)
+
+"""
+def modify_model(model):
+    if len(all_instances(model, "Design/ProbeBlock")) > 2:
+        import time
+        print("Sufficient Probeblocks detected, so pausing with edits")
+        while 1:
+            time.sleep(1)
+
+    new_block = instantiate(model, "Design/ProbeBlock")
+    attr_assign(model, new_block, "name", "MyProbeBlock")
+    print("Added new probeblock: " + new_block)
+
+def idle(model):
+    import time
+    while 1:
+        time.sleep(1)
+
+#process_execute("models/live_modelling_CBD", {"design_model": "models/CBD_spring"}, {"models/CBD/edit": modify_model})
+#process_execute("models/live_modelling_CBD", {"design_model": "models/CBD_spring"}, {"models/CBD/edit": idle})
+
+model_add("formalisms/PW_Plant", "formalisms/SimpleClassDiagrams", open("models/plant_PW.mvc", 'r').read())
+model_add("models/plant", "formalisms/PW_Plant", open("models/plant_model.mvc", 'r').read().replace("PW_Plant/", ""))
+model_add("formalisms/Requirements", "formalisms/SimpleClassDiagrams", open("models/requirements.mvc", 'r').read())
+model_add("models/requirements", "formalisms/Requirements", open("models/requirements_model.mvc", 'r').read().replace("Requirements/", ""))
+model_add("formalisms/Encapsulated_PetriNet", "formalisms/SimpleClassDiagrams", open("models/petrinet_ports.mvc", 'r').read())
+transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Plant": "formalisms/PW_Plant"}, {"PW_Plant": "formalisms/PW_Plant"}, "models/revise_plant")
+
+def tracability_PLANT2EPN(model):
+    instantiate(model, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link")
+    instantiate(model, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink")
+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)
+
 model_add("formalisms/ParallelDEVS", "formalisms/SimpleClassDiagrams", open("models/paralleldevs_design.mvc").read())
 model_add("models/produce_consume_pdevs", "formalisms/ParallelDEVS", open("models/produce_consume_PDEVS.mvc").read())
 transformation_add_AL({"ParallelDEVS": "formalisms/ParallelDEVS"}, {}, "models/paralleldevs_simulator", open("models/pdevs_client.alc", "r").read())
 
-"""
 model_add("formalisms/ReachabilityGraph", "formalisms/SimpleClassDiagrams", open("models/reachability_graph.mvc", "r").read())
 model_add("formalisms/PetriNet", "formalisms/SimpleClassDiagrams", open("integration/code/pn_design.mvc", 'r').read())
-model_add("formalisms/Encapsulated_PetriNet", "formalisms/SimpleClassDiagrams", open("models/petrinet_ports.mvc", 'r').read())
-model_add("formalisms/PW_Plant", "formalisms/SimpleClassDiagrams", open("models/plant_PW.mvc", 'r').read())
 model_add("formalisms/PW_Environment", "formalisms/SimpleClassDiagrams", open("models/environment_PW.mvc", 'r').read())
 model_add("formalisms/PW_Control", "formalisms/SimpleClassDiagrams", open("models/control_PW.mvc", 'r').read())
-model_add("formalisms/Requirements", "formalisms/SimpleClassDiagrams", open("models/requirements.mvc", 'r').read())
 model_add("formalisms/Query", "formalisms/SimpleClassDiagrams", open("models/query.mvc", 'r').read())
 model_add("formalisms/Architecture", "formalisms/SimpleClassDiagrams", open("models/architecture.mvc", 'r').read())
 
 model_add("models/pm_powerwindow", "formalisms/ProcessModel", open("models/pm_req_analyse.mvc", 'r').read())
 
-model_add("models/plant", "formalisms/PW_Plant", open("models/plant_model.mvc", 'r').read().replace("PW_Plant/", ""))
 model_add("models/environment", "formalisms/PW_Environment", open("models/environment_model.mvc", 'r').read().replace("PW_Environment/", ""))
 model_add("models/control", "formalisms/PW_Control", open("models/control_model.mvc", 'r').read().replace("PW_Control/", ""))
 model_add("models/control_wrong", "formalisms/PW_Control", open("models/control_model_wrong.mvc", 'r').read().replace("PW_Control/", ""))
 model_add("models/query", "formalisms/Query", open("models/query_model.mvc", 'r').read().replace("Query/", ""))
 model_add("models/architecture", "formalisms/Architecture", open("models/architecture_model.mvc", 'r').read().replace("Architecture/", ""))
-model_add("models/requirements", "formalisms/Requirements", open("models/requirements_model.mvc", 'r').read().replace("Requirements/", ""))
 
 transformation_add_MANUAL({"Requirements": "formalisms/Requirements"}, {"Requirements": "formalisms/Requirements"}, "models/revise_req")
 transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Environment": "formalisms/PW_Environment"}, {"PW_Environment": "formalisms/PW_Environment"}, "models/revise_environment")
-transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Plant": "formalisms/PW_Plant"}, {"PW_Plant": "formalisms/PW_Plant"}, "models/revise_plant")
 transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Control": "formalisms/PW_Control"}, {"PW_Control": "formalisms/PW_Control"}, "models/revise_control")
 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(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_CTRL2EPN(model):
+    instantiate(model, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link")
+    instantiate(model, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_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(model):
+    instantiate(model, "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(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)
+def tracability_EPN2PN(model):
+    instantiate(model, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
+    instantiate(model, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
 
 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)
 transformation_add_MT({"PW_Control": "formalisms/PW_Control"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/control_to_EPN", open("models/control_to_EPN.mvc", 'r').read(), tracability_CTRL2EPN)
 transformation_add_MT({"PW_Environment": "formalisms/PW_Environment"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/environment_to_EPN", open("models/environment_to_EPN.mvc", 'r').read(), tracability_ENV2EPN)
 transformation_add_MT({"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet", "Architecture": "formalisms/Architecture"}, {"PetriNet": "formalisms/PetriNet"}, "models/combine_EPN", open("models/combine_EPN.mvc", 'r').read(), tracability_EPN2PN)

+ 1 - 0
scripts/run_local_modelverse.py

@@ -32,6 +32,7 @@ try:
     server = subprocess.Popen(program_to_execute)
 
     server.wait()
+
 finally:
     # Stop the server
     try:

+ 0 - 1
services/HUTN/main.py

@@ -8,7 +8,6 @@ from modelverse import *
 import os
 
 import time
-time.sleep(1)
 init(sys.argv[1])
 login("HUTN", "HUTN")
 

+ 0 - 1
services/JSON/main.py

@@ -5,7 +5,6 @@ import os
 import json
 
 import time
-time.sleep(1)
 init(sys.argv[1])
 login("JSON", "JSON")
 

+ 6 - 0
services/LoLA/install.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+tar -xvzf lola-2.0.tar.gz
+cd lola-2.0
+./configure
+make -j4
+ln -s lola-2.0/src/lola lola

BIN
services/LoLA/lola-2.0.tar.gz


+ 40 - 0
services/LoLA/main.py

@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+import sys
+sys.path.append("wrappers")
+
+from modelverse import *
+from subprocess import Popen, PIPE
+import json
+
+init(sys.argv[1])
+login("test_service", "my_password")
+
+def lola_service(port):
+    json_input = service_get(port)
+    input_data = json.loads(json_input)
+    print input_data['petrinet']
+    print input_data['query']
+    query_formula = "--formula=EF ("+ input_data['query']+")"
+    lola = Popen(["lola", "--path=path_output", query_formula, "--json=output.json"], stdin=PIPE, stdout=PIPE).communicate(input=input_data['petrinet'])
+    
+    output_file = open('output.json', 'r')
+    result = json.load(output_file)
+    output_file.close()
+    # If safety query is violated resulting True in output
+    path = ''
+    if result['analysis']['result']:
+        with open('path_output') as f:
+            for transition in f:
+                transition = transition.replace('\n',',')
+                path = path + transition
+    #json_result = '{"analysis_result": True , "path":\"'+path.rstrip(',') + '\"}'
+    service_set(port, result['analysis']['result'])
+    service_set(port, path.rstrip(','))
+
+service_register("lola", lola_service)
+
+while raw_input() != "STOP":
+    # Stay active, as we shouldn't exit while the service is running!
+    pass
+
+service_stop()

+ 8 - 2
unit/log_output.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:   Fri Nov 10 08:52:06 2017
+Date:   Mon Nov 13 12:46:02 2017
 
 Model author: Yentl Van Tendeloo
 Model name:   Logging
@@ -65,6 +65,7 @@ class Logging(RuntimeClassBase):
         _init_1.setTrigger(Event("_0after"))
         self.states["/init"].addTransition(_init_1)
         _init_2 = Transition(self, self.states["/init"], [self.states["/finished"]])
+        _init_2.setAction(self._init_2_exec)
         _init_2.setTrigger(Event("terminate", "inp"))
         self.states["/init"].addTransition(_init_2)
     
@@ -77,6 +78,10 @@ class Logging(RuntimeClassBase):
     def _init_0_exec(self, parameters):
         value = parameters[0]
         self.log.append(value)
+        print("Logging " + str(value))
+    
+    def _init_2_exec(self, parameters):
+        print("Got terminate")
     
     def initializeStatechart(self):
         # enter default state
@@ -102,4 +107,5 @@ class Controller(ThreadsControllerBase):
         ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
         self.addInputPort("inp")
         self.addOutputPort("outp")
-        self.object_manager.createInstance("Logging", [log])
+        self.object_manager.createInstance("Logging", [log])
+        print("Start SC at " + str(self))

+ 7 - 1
unit/log_output.xml

@@ -14,18 +14,24 @@
                 self.log = log
             </body>
         </constructor>
+
         <scxml initial="init">
             <state id="init">
                 <transition event="input" port="inp" target=".">
                     <parameter name="value"/>
                     <script>
+                        print("Logging " + str(value))
                         self.log.append(value)
                     </script>
                 </transition>
 
                 <transition after="0.1" target="."/>
 
-                <transition event="terminate" port="inp" target="../finished"/>
+                <transition event="terminate" port="inp" target="../finished">
+                    <script>
+                        print("Got terminate")
+                    </script>
+                </transition>
             </state>
 
             <state id="finished"/>

+ 40 - 37
unit/test_all.py

@@ -173,38 +173,38 @@ class TestModelverse(unittest.TestCase):
     def test_operations(self):
         log = []
 
-        def manual_callback(context):
-            p1 = instantiate(None, "PetriNet_Runtime/Place", context=context)
-            attr_assign(None, p1, "tokens", 1, context=context)
-            attr_assign(None, p1, "name", "p1", context=context)
-            p2 = instantiate(None, "PetriNet_Runtime/Place", context=context)
-            attr_assign(None, p2, "tokens", 2, context=context)
-            attr_assign(None, p2, "name", "p2", context=context)
-            p3 = instantiate(None, "PetriNet_Runtime/Place", context=context)
-            attr_assign(None, p3, "tokens", 3, context=context)
-            attr_assign(None, p3, "name", "p3", context=context)
-            t1 = instantiate(None, "PetriNet_Runtime/Transition", context=context)
-            attr_assign(None, t1, "name", "t1", context=context)
-            attr_assign(None, t1, "executing", False, context=context)
-            p2t1 = instantiate(None, "PetriNet_Runtime/P2T", (p1, t1), context=context)
-            attr_assign(None, p2t1, "weight", 1, context=context)
-            p2t2 = instantiate(None, "PetriNet_Runtime/P2T", (p2, t1), context=context)
-            attr_assign(None, p2t2, "weight", 1, context=context)
-            t2p1 = instantiate(None, "PetriNet_Runtime/T2P", (t1, p3), context=context)
-            attr_assign(None, t2p1, "weight", 2, context=context)
+        def manual_callback(model):
+            p1 = instantiate(model, "PetriNet_Runtime/Place")
+            p2 = instantiate(model, "PetriNet_Runtime/Place")
+            p3 = instantiate(model, "PetriNet_Runtime/Place")
+            t1 = instantiate(model, "PetriNet_Runtime/Transition")
+            p2t1 = instantiate(model, "PetriNet_Runtime/P2T", (p1, t1))
+            p2t2 = instantiate(model, "PetriNet_Runtime/P2T", (p2, t1))
+            t2p1 = instantiate(model, "PetriNet_Runtime/T2P", (t1, p3))
+            attr_assign(model, p1, "tokens", 1)
+            attr_assign(model, p1, "name", "p1")
+            attr_assign(model, p2, "tokens", 2)
+            attr_assign(model, p2, "name", "p2")
+            attr_assign(model, p3, "tokens", 3)
+            attr_assign(model, p3, "name", "p3")
+            attr_assign(model, t1, "name", "t1")
+            attr_assign(model, t1, "executing", False)
+            attr_assign(model, p2t1, "weight", 1)
+            attr_assign(model, p2t2, "weight", 1)
+            attr_assign(model, t2p1, "weight", 2)
 
         model_add("test/PetriNet", "formalisms/SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
         model_add("test/PetriNet_Runtime", "formalisms/SimpleClassDiagrams", open("integration/code/pn_runtime.mvc", "r").read())
 
         model_add("test/my_pn", "test/PetriNet", open("integration/code/pn_design_model.mvc", "r").read())
 
-        def add_tracability_D2R(context):
-            instantiate(None, "Association", ("PetriNet/Place", "PetriNet_Runtime/Place"), ID="D2R_PlaceLink", context=context)
-            instantiate(None, "Association", ("PetriNet/Transition", "PetriNet_Runtime/Transition"), ID="D2R_TransitionLink", context=context)
+        def add_tracability_D2R(model):
+            instantiate(model, "Association", ("PetriNet/Place", "PetriNet_Runtime/Place"), ID="D2R_PlaceLink")
+            instantiate(model, "Association", ("PetriNet/Transition", "PetriNet_Runtime/Transition"), ID="D2R_TransitionLink")
 
-        def add_tracability_R2D(context):
-            instantiate(None, "Association", ("PetriNet_Runtime/Place", "PetriNet/Place"), ID="R2D_PlaceLink", context=context)
-            instantiate(None, "Association", ("PetriNet_Runtime/Transition", "PetriNet/Transition"), ID="R2D_TransitionLink", context=context)
+        def add_tracability_R2D(model):
+            instantiate(model, "Association", ("PetriNet_Runtime/Place", "PetriNet/Place"), ID="R2D_PlaceLink")
+            instantiate(model, "Association", ("PetriNet_Runtime/Transition", "PetriNet/Transition"), ID="R2D_TransitionLink")
 
         transformation_add_MT({"PetriNet": "test/PetriNet"}, {}, "test/print_pn", open("integration/code/pn_print.mvc").read())
         transformation_add_MANUAL({"PetriNet": "test/PetriNet"}, {"PetriNet_Runtime": "test/PetriNet_Runtime"}, "test/pn_design_to_runtime", add_tracability_D2R)
@@ -218,10 +218,11 @@ class TestModelverse(unittest.TestCase):
         thrd.daemon = True
         thrd.start()
 
-        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == None
+        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == True
         print("Joining")
         thrd.join()
         print("Joined")
+        print("Got log: " + str(log))
         assert set(log) == set(['"p1" --> 1',
                                 '"p2" --> 2',
                                 '"p3" --> 3'])
@@ -239,7 +240,7 @@ class TestModelverse(unittest.TestCase):
         thrd.daemon = True
         thrd.start()
 
-        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == None
+        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == True
         thrd.join()
         assert set(log) == set(['"p1" --> 0',
                                 '"p2" --> 1',
@@ -261,16 +262,18 @@ class TestModelverse(unittest.TestCase):
         transformation_add_AL({"PetriNet": "test/PetriNet"}, {"ReachabilityGraph": "test/ReachabilityGraph"}, "test/reachability", open("integration/code/reachability_subfunction.alc", "r").read())
         transformation_add_MT({"ReachabilityGraph": "test/ReachabilityGraph"}, {}, "test/reachability_print", open("integration/code/reachabilitygraph_print.mvc", 'r').read())
 
-        def callback_refine_PN(context):
-            p1 = instantiate(None, "PetriNet/Place", context=context)
-            attr_assign(None, p1, "name", "p1", context=context)
-            attr_assign(None, p1, "tokens", 1, context=context)
+        def callback_refine_PN(model):
+            print("MANUAL CODE")
+            p1 = instantiate(model, "PetriNet/Place")
+            attr_assign(model, p1, "name", "p1")
+            attr_assign(model, p1, "tokens", 1)
 
-            t1 = instantiate(None, "PetriNet/Transition", context=context)
-            attr_assign(None, t1, "name", "t1", context=context)
+            t1 = instantiate(model, "PetriNet/Transition")
+            attr_assign(model, t1, "name", "t1")
 
-            p2t = instantiate(None, "PetriNet/P2T", (p1, t1), context=context)
-            attr_assign(None, p2t, "weight", 1, context=context)
+            p2t = instantiate(model, "PetriNet/P2T", (p1, t1))
+            attr_assign(model, p2t, "weight", 1)
+            print("END MANUAL CODE")
 
         import log_output
         log = []
@@ -298,8 +301,8 @@ class TestModelverse(unittest.TestCase):
         model_add("test/MM_rendered_graphical", "formalisms/SimpleClassDiagrams", open("models/MM_rendered_graphical.mvc", 'r').read())
         model_add("test/my_CBD", "test/CausalBlockDiagrams", open("integration/code/my_cbd.mvc", 'r').read())
 
-        def add_tracability(context):
-            instantiate(None, "Association", ("abstract/Block", "rendered/Group"), ID="TracabilityLink", context=context)
+        def add_tracability(model):
+            instantiate(model, "Association", ("abstract/Block", "rendered/Group"), ID="TracabilityLink")
 
         transformation_add_MT({"abstract": "test/CausalBlockDiagrams", "rendered": "test/MM_rendered_graphical"}, {"abstract": "test/CausalBlockDiagrams", "rendered": "test/MM_rendered_graphical"}, "test/render_graphical_CBD", open("models/CBD_mapper.mvc", 'r').read(), add_tracability)
         result = model_render("test/my_CBD", "test/render_graphical_CBD", "test/my_perceptualized_CBD")

+ 1 - 0
unit/utils.py

@@ -78,5 +78,6 @@ def flush_data(address, data):
 def start_mvc():
     port = getFreePort()
     address = "127.0.0.1:%s" % port
+    print("Execute run local MV")
     proc = execute("run_local_modelverse", [str(port)], wait=False)
     return proc, address

File diff suppressed because it is too large
+ 156 - 225
wrappers/classes/modelverse.xml


+ 141 - 99
wrappers/modelverse.py

@@ -52,38 +52,103 @@ def _next_ID():
     ID += 1
     return ID
 
-def _process_SC(statechart, port_sc, context):
-    print("Context: " + str(context))
+def __run_new_modelverse(address, username, password, callback, model):
+    init(address)
+    login(username, password)
+    callback(model)
+    exit_save(model)
+
+def __run_new_modelverse_activity(address, username, password, taskname, pipe, callback):
+    init(address, taskname=taskname)
+    controller.username = username
+    controller.password = password
+    t = OUTPUT()
+
+    if t == "OP":
+        model = OUTPUT()
+        if callback is not None:
+            __invoke(callback, model)
+        controller.addInput(Event("data_input", "action_in", [None, None]))
+        time.sleep(2)
+    elif t == "SC":
+        while 1:
+            empty = True
+
+            # Fetch output from the MV
+            response = responses.fetch(0)
+            if response is not None:
+                print("Got response: " + str(response))
+                if response.name == "data_output":
+                    # Got output of MV, so forward to SCCD
+                    if pipe is not None:
+                        pipe.send(("input", response.parameters))
+                elif response.name == "result":
+                    # Finished execution, so continue and return result
+                    if pipe is not None:
+                        print("Sending over pipe!")
+                        pipe.send(("terminate", []))
+                        pipe.close()
+                    print("TERMINATE")
+                    return
+                else:
+                    raise Exception("Unknown data from MV to SC: " + str(response))
+                empty = False
+
+            # Fetch output from the SC
+            if pipe is not None and pipe.poll():
+                response = pipe.recv()
+
+                if response.name == "output":
+                    controller.addInput(Event("data_input", "action_in", [response.parameters, context]))
+                else:
+                    raise Exception("Unknown data from SC to MV: " + str(response))
+                empty = False
+
+            if empty:
+                time.sleep(0.05)
+
+def __invoke(callback, model):
+    import multiprocessing
+    p = multiprocessing.Process(target=__run_new_modelverse, args=[controller.address, controller.username, controller.password, callback, model])
+    p.start()
+    p.join()
+
+def _process_SC(statechart, port_sc, taskname):
+    import multiprocessing
+    p2c_pipe, c2p_pipe = multiprocessing.Pipe()
+    p = multiprocessing.Process(target=__run_new_modelverse_activity, args=[controller.address, controller.username, controller.password, taskname, c2p_pipe, None])
+    p.start()
     while 1:
         empty = True
 
-        # Fetch output from the MV
-        response = responses.fetch(0)
-        if response is not None:
-            print("Output of MV to SC")
-            if response.name == "data_output":
-                # Got output of MV, so forward to SCCD
-                statechart[0].addInput(Event("input", statechart[1], response.parameters))
-            elif response.name == "result":
-                # Finished execution, so continue and return result
-                statechart[0].addInput(Event("terminate", statechart[1], []))
-                return response.parameters[1]
-            else:
-                raise Exception("Unknown data from MV to SC: " + str(response))
+        if p2c_pipe.poll():
+            response = p2c_pipe.recv()
+            print("Got SC response: " + str(response))
+            statechart[0].addInput(Event(response[0], statechart[1], response[1]))
+            
+            if response[0] == "terminate":
+                print("Got break")
+                p2c_pipe.close()
+                break
             empty = False
 
-        # Fetch output from the SC
         response = port_sc.fetch(0)
         if response is not None:
-            print("Output of SC to MV")
-            if response.name == "output":
-                controller.addInput(Event("data_input", "action_in", [response.parameters, context]))
-            else:
-                raise Exception("Unknown data from SC to MV: " + str(response))
+            p2c_pipe.send(response)
             empty = False
 
         if empty:
-            time.sleep(0.5)
+            time.sleep(0.05)
+
+    print("Wait for join")
+    p.join()
+    print("Joined SC")
+
+def _process_OP(callback, taskname):
+    import multiprocessing
+    p = multiprocessing.Process(target=__run_new_modelverse_activity, args=[controller.address, controller.username, controller.password, taskname, None, callback])
+    p.start()
+    p.join()
 
 def INPUT(action, context, parameters):
     controller.addInput(Event("action", "action_in", [action, _next_ID(), context, parameters]))
@@ -102,12 +167,12 @@ def OUTPUT():
                 print("Unknown error: " + str(response.parameters))
                 raise UnknownError()
 
-def init(address_param="127.0.0.1:8001", timeout=20.0):
+def init(address_param="127.0.0.1:8001", timeout=20.0, taskname=None):
     global controller
     global ID
     global responses
 
-    controller = modelverse_SCCD.Controller()
+    controller = modelverse_SCCD.Controller(taskname)
     socket2event.boot_translation_service(controller)
 
     ID = 0
@@ -124,6 +189,8 @@ def init(address_param="127.0.0.1:8001", timeout=20.0):
     return OUTPUT()
 
 def login(username, password):
+    controller.username = username
+    controller.password = password
     INPUT("login", None, [username, password])
     return OUTPUT()
 
@@ -147,8 +214,8 @@ def verify(model_name, metamodel_name):
     INPUT("verify", None, [model_name, metamodel_name])
     return OUTPUT()
 
-def model_overwrite(model_name, new_model, context=None):
-    INPUT("model_overwrite", context, [model_name, new_model])
+def model_overwrite(model_name, new_model):
+    INPUT("model_overwrite", None, [model_name, new_model])
     return OUTPUT()
 
 def disconnect():
@@ -173,31 +240,35 @@ def transformation_between(sources, targets):
 
 def transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=None):
     INPUT("transformation_add_MT", None, [source_metamodels, target_metamodels, operation_name, code, True])
-    context, model = OUTPUT()
+    model = OUTPUT()
+
     if callback is not None:
-        callback(context)
-    INPUT("exit", context, [])
+        __invoke(callback, model)
+    controller.addInput(Event("data_input", "action_in", [None, None]))
+
     return OUTPUT()
 
 def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=None):
     INPUT("transformation_add_AL", None, [source_metamodels, target_metamodels, operation_name, code, True])
-    context, model = OUTPUT()
+    model = OUTPUT()
 
-    if context is None:
-        # In case the source and target metamodels are empty, the context will be None, indicating that we are finished already (no callbacks allowed)
-        return
+    if model is not None:
+        # In case the source and target metamodels are empty, the model will be None, indicating that we are finished already (no callbacks allowed)
+
+        if callback is not None:
+            __invoke(callback, model)
+        controller.addInput(Event("data_input", "action_in", [None, None]))
 
-    if callback is not None:
-        callback(context)
-    INPUT("exit", context, [])
     return OUTPUT()
 
 def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=None):
     INPUT("transformation_add_MANUAL", None, [source_metamodels, target_metamodels, operation_name, True])
-    context, model = OUTPUT()
+    model = OUTPUT()
+
     if callback is not None:
-        callback(context)
-    INPUT("exit", context, [])
+        __invoke(callback, model)
+    controller.addInput(Event("data_input", "action_in", [None, None]))
+
     return OUTPUT()
 
 def __transformation_execute(operation_name, input_models_dict, output_models_dict, statechart, tracability_model, fetch_output):
@@ -205,13 +276,10 @@ def __transformation_execute(operation_name, input_models_dict, output_models_di
         port_sc = statechart[0].addOutputListener(statechart[2])
 
     INPUT("transformation_execute", None, [operation_name, input_models_dict, output_models_dict, tracability_model, fetch_output])
-    op, name, context = OUTPUT()
+    taskname = OUTPUT()
     if statechart is not None:
-        threading.Thread(target=_process_SC, args=[statechart, port_sc, context]).start()
-    else:
-        val = OUTPUT()
-        print("Transformation result: " + str(val))
-        return val
+        threading.Thread(target=_process_SC, args=[statechart, port_sc, taskname]).start()
+    return OUTPUT()
 
 def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, statechart=None, tracability_model="", fetch_output=True):
     return __transformation_execute(operation_name, input_models_dict, output_models_dict, statechart, tracability_model, fetch_output)
@@ -221,10 +289,10 @@ def transformation_execute_AL(operation_name, input_models_dict, output_models_d
 
 def transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=None, tracability_model=""):
     INPUT("transformation_execute", None, [operation_name, input_models_dict, output_models_dict, tracability_model])
-    op, name, context, model = OUTPUT()
-    if callback is not None:
-        callback(context)
-    INPUT("exit", context, [])
+    taskname = OUTPUT()
+
+    _process_OP(callback, taskname)
+
     return OUTPUT()
 
 def transformation_signature(operation_name):
@@ -374,71 +442,45 @@ def all_instances(model_name, type_name, context=None):
     INPUT("all_instances", context, [model_name, type_name])
     return OUTPUT()
 
-def process_execute(process_name, prefix, callbacks=None):
+def process_execute(process_name, model_mapping, callbacks=None):
     # for all callbacks to SCs, start up the output port already
     sc_ports = {}
-
     for k, v in callbacks.items():
         if isinstance(v, (tuple, list)):
             # Is a statechart, so register already
             sc_ports[k] = v[0].addOutputListener(v[2])
 
-    INPUT("process_execute", None, [process_name, prefix])
+    INPUT("process_execute", None, [process_name, model_mapping])
 
-    operation = OUTPUT()
     while 1:
-        if isinstance(operation, (list, tuple)):
-            if operation[0] == "OP":
-                t, name, context, model = operation
-                if name in callbacks:
-                    callbacks[name](context)
-                INPUT("exit", context, [])
-                operation = OUTPUT()
-            elif operation[0] == "SC":
-                t, name, context = operation
-                if name in callbacks:
-                    statechart = callbacks[name]
-                else:
-                    statechart = None
-
-                while 1:
-                    empty = True
-
-                    # Fetch output from the MV
-                    response = responses.fetch(0)
-                    if response is not None:
-                        if response.name == "data_output":
-                            # Got output of MV, so forward to SCCD
-                            if statechart:
-                                statechart[0].addInput(Event("input", statechart[1], response.parameters))
-                        elif response.name == "result":
-                            # Finished execution, so continue and return result
-                            if statechart:
-                                statechart[0].addInput(Event("terminate", statechart[1], []))
-                            # Break from the most inner loop
-                            operation = response.parameters[1]
-                            break
-                        empty = False
-
-                    # Fetch output from the SC
-                    if statechart:
-                        response = sc_ports[name].fetch(0)
-                        if response is not None:
-                            if response.name == "output":
-                                controller.addInput(Event("data_input", "action_in", [response.parameters, context]))
-                            empty = False
-
-                    if empty:
-                        time.sleep(0.01)
+        result = OUTPUT()
+        if result == "Success":
+            # Finished
+            return None
         else:
-            if operation == "Finished":
-                # Finished execution of the process, so exit
-                return None
+            taskname, operation = result
+
+            if (operation in callbacks):
+                data = callbacks[operation]
+
+                if isinstance(data, (tuple, list)):
+                    # Statechart, so consider like that
+                    threading.Thread(target=_process_SC, args=[data, sc_ports[operation], taskname]).start()
+                else:
+                    # Assume function
+                    threading.Thread(target=_process_OP, args=[data, taskname]).start()
+            else:
+                # Assume empty function
+                threading.Thread(target=_process_OP, args=[None, taskname]).start()
 
 def get_taskname():
     """Fetch the taskname of the current connection."""
     return controller.taskname
 
+def exit_save(model_name):
+    INPUT("exit_save", None, [model_name])
+    return OUTPUT()
+
 """ Some hardcoded functions... Way easier to express them with code than with statecharts!"""
 import json
 import urllib

File diff suppressed because it is too large
+ 447 - 608
wrappers/modelverse_SCCD.py


+ 1 - 1
wrappers/poll_print.xml

@@ -35,7 +35,7 @@
 
             <state id="finished">
                 <script>
-                    print("FINISHED")
+                    print("FINISHED SC")
                 </script>
             </state>
         </scxml>