Browse Source

Merge branch 'testing' into MvK_rules

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

+ 272 - 175
bootstrap/core_algorithm.alc

@@ -14,12 +14,14 @@ include "conformance_finding.alh"
 include "typing.alh"
 include "typing.alh"
 include "compiler.alh"
 include "compiler.alh"
 include "random.alh"
 include "random.alh"
+include "io.alh"
 
 
 String core_model_location = "models/core"
 String core_model_location = "models/core"
 
 
 Element core = ?
 Element core = ?
 String current_user_id
 String current_user_id
 Element caches
 Element caches
+Element pm_tasks = ?
 
 
 String function get_foldername(name : String):
 String function get_foldername(name : String):
 	Element result
 	Element result
@@ -465,20 +467,6 @@ Boolean function check_conformance(model_id : String):
 
 
 	return True!
 	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):
 Element function merge_models(models_dict : Element, operation_name : String):
 	core = import_node(core_model_location)
 	core = import_node(core_model_location)
 	// 0) Find operation signature
 	// 0) Find operation signature
@@ -642,7 +630,7 @@ Element function execute_operation(operation_id : String, input_models : Element
 	while (set_len(iter) > 0):
 	while (set_len(iter) > 0):
 		edge = set_pop(iter)
 		edge = set_pop(iter)
 		dict_add(output_metamodels, read_attribute(core, edge, "name"), full_name(readAssociationDestination(core, edge)))
 		dict_add(output_metamodels, read_attribute(core, edge, "name"), full_name(readAssociationDestination(core, edge)))
-	
+
 	// 1) Find merged metamodel
 	// 1) Find merged metamodel
 
 
 	exact_type = read_type(core, operation_id)
 	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):
 		while (set_len(keys) > 0):
 			key = set_pop(keys)
 			key = set_pop(keys)
 			Element mm
 			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]))
 			mm = get_full_model(get_entry_id(input_models[key]), get_entry_id(input_metamodels[key]))
 			if (element_eq(mm, read_root())):
 			if (element_eq(mm, read_root())):
 				log("Signature mismatch in operation for tag " + key)
 				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) != ""):
 		while (get_entry_id(model_name) != ""):
 			model_name = ".tmp/" + random_string(20)
 			model_name = ".tmp/" + random_string(20)
 		model_create(merged_model, model_name, merged_metamodel_id, "Model")
 		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
 		// 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))
 		//model_delete(get_entry_id(model_name))
 		result = True
 		result = True
 	elif (exact_type == "ActionLanguage"):
 	elif (exact_type == "ActionLanguage"):
@@ -790,17 +778,22 @@ Boolean function enact_action(pm : Element, element : String, mapping : Element)
 	Element output_mms
 	Element output_mms
 	Element consumes_link
 	Element consumes_link
 	String name
 	String name
-	String value
 	String elem_name
 	String elem_name
 	Element keys
 	Element keys
 	String key
 	String key
 	String consume
 	String consume
 	String produce
 	String produce
+	Element input_traceability_model
+	String output_traceability_name
+	String output_traceability_type
 	Element output_map
 	Element output_map
 
 
 	inputs = dict_create()
 	inputs = dict_create()
 	outputs = dict_create()
 	outputs = dict_create()
 	output_map = 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
 	// Read out the referenced element from the MvC
 	transformation_id = get_entry_id(read_attribute(pm, element, "name"))
 	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")
 	lst = allOutgoingAssociationInstances(pm, element, "Consumes")
 	while (set_len(lst) > 0):
 	while (set_len(lst) > 0):
 		consume = set_pop(lst)
 		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
 	// Find all output model names and their metamodel
 	lst = allOutgoingAssociationInstances(pm, element, "Produces")
 	lst = allOutgoingAssociationInstances(pm, element, "Produces")
@@ -819,33 +823,46 @@ Boolean function enact_action(pm : Element, element : String, mapping : Element)
 		elem = readAssociationDestination(pm, produce)
 		elem = readAssociationDestination(pm, produce)
 		type_name = read_attribute(pm, elem, "type")
 		type_name = read_attribute(pm, elem, "type")
 		elem_name = read_attribute(pm, elem, "name")
 		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":
 	if read_type(core, transformation_id) == "ActionLanguage":
 		log(string_join("Enacting ActionLanguage: ", read_attribute(pm, element, "name")))
 		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":
 	elif read_type(core, transformation_id) == "ManualOperation":
 		log(string_join("Enacting ManualOperation: ", read_attribute(pm, element, "name")))
 		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:
 	else:
 		log(string_join("Enacting ModelTransformation: ", read_attribute(pm, element, "name")))
 		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())):
 	if (element_eq(result, read_root())):
 		// Something went wrong!
 		// Something went wrong!
+		output("Failure")
 		return False!
 		return False!
 	else:
 	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)
 		keys = dict_keys(outputs)
 		while (set_len(keys) > 0):
 		while (set_len(keys) > 0):
 			key = set_pop(keys)
 			key = set_pop(keys)
+			log("Overwriting model at " + key)
 			if (get_entry_id(output_map[key]) == ""):
 			if (get_entry_id(output_map[key]) == ""):
 				// New model
 				// New model
 				model_create(result[key], output_map[key], get_entry_id(outputs[key]), "Model")
 				model_create(result[key], output_map[key], get_entry_id(outputs[key]), "Model")
 			else:
 			else:
 				model_overwrite(result[key], get_entry_id(output_map[key]), get_entry_id(outputs[key]))
 				model_overwrite(result[key], get_entry_id(output_map[key]), get_entry_id(outputs[key]))
+		output("Success")
 		return True!
 		return True!
 
 
 Element function PM_signature(pm : Element):
 Element function PM_signature(pm : Element):
@@ -861,6 +878,18 @@ Element function PM_signature(pm : Element):
 
 
 	return result!
 	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):
 Void function enact_PM(pm : Element, mapping : Element):
 	Element worklist
 	Element worklist
 	String element
 	String element
@@ -869,6 +898,7 @@ Void function enact_PM(pm : Element, mapping : Element):
 	Element tuple
 	Element tuple
 	Element counters
 	Element counters
 	Element join_nodes
 	Element join_nodes
+	Element exec_nodes
 	Element keys
 	Element keys
 	String key
 	String key
 
 
@@ -889,6 +919,8 @@ Void function enact_PM(pm : Element, mapping : Element):
 			while (get_entry_id(mock_location) != ""):
 			while (get_entry_id(mock_location) != ""):
 				mock_location = ".tmp/" + random_string(10)
 				mock_location = ".tmp/" + random_string(10)
 			dict_add(mapping, key, mock_location)
 			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)
 			set_add(mock_locations, mock_location)
 
 
 	// Initialize Join counters
 	// Initialize Join counters
@@ -897,76 +929,117 @@ Void function enact_PM(pm : Element, mapping : Element):
 	while (set_len(join_nodes) > 0):
 	while (set_len(join_nodes) > 0):
 		dict_add(counters, set_pop(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
 	// Create the worklist with the Start instance as first element
 	worklist = set_create()
 	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!
 				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
 	// Remove all mock locations again
 	while (set_len(mock_locations) > 0):
 	while (set_len(mock_locations) > 0):
@@ -1039,30 +1112,25 @@ String function cmd_model_add(type : String, name : String, code : String):
 		// Type exists
 		// Type exists
 		if (allow_read(current_user_id, type_id)):
 		if (allow_read(current_user_id, type_id)):
 			// And is readable
 			// 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:
 			else:
-				return "No such folder: " + get_foldername(name)!
+				return "Model exists: " + name!
 		else:
 		else:
 			return "Permission denied to model: " + type!
 			return "Permission denied to model: " + type!
 	else:
 	else:
@@ -1306,6 +1374,90 @@ String function cmd_model_render(model_name : String, mapper_name : String, rend
 	else:
 	else:
 		return "Model not found: " + model_name!
 		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):
 String function cmd_transformation_execute(transformation_name : String, source_models : Element, target_models : Element, tracability_name : String):
 	// Execute a transformation, whatever type it is
 	// Execute a transformation, whatever type it is
 	// First we detect the type, so we know how to prepare for invocation
 	// 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 target_model_name
 	String source_model_name
 	String source_model_name
 	String source_model_ID
 	String source_model_ID
-	Element result
-	Element keys
-	String key
 	String assoc_name
 	String assoc_name
 
 
 	transformation_id = get_entry_id(transformation_name)
 	transformation_id = get_entry_id(transformation_name)
@@ -1381,54 +1530,10 @@ String function cmd_transformation_execute(transformation_name : String, source_
 							else:
 							else:
 								return "Permission denied to model: " + target_model_name!
 								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:
 				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"!
 					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:
 			else:
 				return "Model is not executable: " + transformation_name!
 				return "Model is not executable: " + transformation_name!
 		else:
 		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):
 		if (dict_len(source_models) + dict_len(target_models) > 0):
 			merged_formalism = model_fuse(formalism_map)
 			merged_formalism = model_fuse(formalism_map)
 			model_create(merged_formalism, "merged/" + operation_name, get_entry_id("formalisms/SimpleClassDiagrams"), "Model")
 			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 (operation_type == "manual"):
 			if (dict_len(source_models) + dict_len(target_models) == 0):
 			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)
 	merged_formalism = model_fuse(to_ramify)
 	model_create(merged_formalism, "merged/" + operation_name, get_entry_id("formalisms/SimpleClassDiagrams"), "Model")
 	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"))
 	model_overwrite(merged_formalism, get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
 
 
 	ramified_metamodel = ramify(merged_formalism)
 	ramified_metamodel = ramify(merged_formalism)
@@ -2357,9 +2456,7 @@ Void function user_function_skip_init(user_id : String):
 		elif (cmd == "folder_create"):
 		elif (cmd == "folder_create"):
 			output(cmd_folder_create(single_input("Folder name?")))
 			output(cmd_folder_create(single_input("Folder name?")))
 		elif (cmd == "add_conformance"):
 		elif (cmd == "add_conformance"):
-			log("Adding conformance relation...")
 			output(cmd_conformance_add(single_input("Model name?"), single_input("Metamodel name?")))
 			output(cmd_conformance_add(single_input("Model name?"), single_input("Metamodel name?")))
-			log("Added!")
 		elif (cmd == "remove_conformance"):
 		elif (cmd == "remove_conformance"):
 			// TODO
 			// TODO
 			cmd = "FAIL"
 			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")
 			src_name = read_attribute(tracability_model, readAssociationSource(tracability_model, tracability_link), "name")
 			dst_name = read_attribute(tracability_model, readAssociationDestination(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")
 			type = read_attribute(tracability_model, tracability_link, "type")
 
 
 			// Now try to find all possible combinations
 			// 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)):
 						if (dict_in(new_model["model"], dst_model + dst_name)):
 							// Found a match for source and target, so create a link
 							// 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)
 							new_name = instantiate_link(new_model, type, "", src_model + src_name, dst_model + dst_name)
+
 							if (new_name == ""):
 							if (new_name == ""):
 								log("ERROR: could not create a tracability link; ignoring")
 								log("ERROR: could not create a tracability link; ignoring")
 	return new_model!
 	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
 	String attr_type
 	Element attr_links
 	Element attr_links
 	String attr_link
 	String attr_link
+	String attr_value
 	
 	
 	attr_type = find_attribute_type(model, element, attribute)
 	attr_type = find_attribute_type(model, element, attribute)
 	attr_links = allOutgoingAssociationInstances(model, element, attr_type)
 	attr_links = allOutgoingAssociationInstances(model, element, attr_type)
 
 
 	while (set_len(attr_links) > 0):
 	while (set_len(attr_links) > 0):
 		attr_link = set_pop(attr_links)
 		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)
 		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])
 		delete_element(model["model"][attr_link])
 
 
 	return!
 	return!

+ 2 - 2
bootstrap/random.alc

@@ -18,13 +18,13 @@ Float function random():
 	seed = integer_modulo(a * seed + c, m)
 	seed = integer_modulo(a * seed + c, m)
 
 
 	// The seed is the new value
 	// 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):
 Integer function random_interval(a : Integer, b : Integer):
 	if (a == b):
 	if (a == b):
 		return a!
 		return a!
 	else:
 	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):
 Element function random_choice(list : Element):
 	if (list_len(list) == 0):
 	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
 	// Additionally, we copy over all globals that we previously had
 	dict_add_fast(task_root, "globals", dict_copy(root[get_taskname()]["globals"]))
 	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")):
 	if (dict_in(function, "params")):
 		// And add the arguments to the symbol table
 		// And add the arguments to the symbol table
 		Element symbols
 		Element symbols
@@ -542,7 +539,6 @@ String function spawn(function : Element, arguments : Element):
 			dict_add(entry, "value", list_pop(arguments, 0))
 			dict_add(entry, "value", list_pop(arguments, 0))
 			t = create_edge(symbols, entry)
 			t = create_edge(symbols, entry)
 			create_edge(t, param_dict[string_get(arg_names_call, arg_i)])
 			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
 			arg_i = arg_i + 1
 
 
 	// Add this only at the end, as otherwise the task will already be detected and executed
 	// 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
 	Element mapping
 	Boolean result
 	Boolean result
 
 
-	//log("Executing rule: " + current)
+	log("Executing rule: " + current)
 
 
 	mappings = full_match(host_model, schedule_model, current, False)
 	mappings = full_match(host_model, schedule_model, current, False)
 
 
@@ -563,7 +563,7 @@ Boolean function transform_forall(host_model : Element, schedule_model : Element
 	else:
 	else:
 		result = False
 		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):
 	while (set_len(mappings) > 0):
 		mapping = set_pop(mappings)
 		mapping = set_pop(mappings)
 		RHS = set_pop(allAssociationDestinations(schedule_model, current, "RHSLink"))
 		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 !
 	return !
 
 
 Void function remove_type(model : Element, name : String):
 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
 	String elem
 	elem = cast_id(model["model"][name])
 	elem = cast_id(model["model"][name])

+ 61 - 39
integration/test_powerwindow.py

@@ -2,6 +2,8 @@ import unittest
 
 
 from utils import *
 from utils import *
 import sys
 import sys
+import os
+import shutil
 
 
 sys.path.append("wrappers")
 sys.path.append("wrappers")
 from modelverse import *
 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", "Query": "formalisms/Query"}, {"Query": "formalisms/Query"}, "models/revise_query")
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Architecture": "formalisms/Architecture"}, {"Architecture": "formalisms/Architecture"}, "models/revise_architecture")
         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", "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_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({"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())
         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
         nr_of_operations = 6
 
 
+        try:
+            shutil.rmtree(".TEST_POWER_WINDOW")
+        except:
+            pass
+        os.makedirs(".TEST_POWER_WINDOW")
+
         def get_function(filename):
         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:
                 if called > nr_of_operations:
                     raise Exception("Seemingly called some operation twice!")
                     raise Exception("Seemingly called some operation twice!")
                 else:
                 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
             return func
 
 
         cb_req = get_function("models/requirements_model.mvc")
         cb_req = get_function("models/requirements_model.mvc")
@@ -98,6 +108,8 @@ class TestPowerWindow(unittest.TestCase):
             import traceback
             import traceback
             print(traceback.format_exc())
             print(traceback.format_exc())
 
 
+        called = len(os.listdir(".TEST_POWER_WINDOW/"))
+        shutil.rmtree(".TEST_POWER_WINDOW")
         if called != 6:
         if called != 6:
             print(called)
             print(called)
             raise Exception("Not executed sufficiently:" + str(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", "Query": "formalisms/Query"}, {"Query": "formalisms/Query"}, "models/revise_query")
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Architecture": "formalisms/Architecture"}, {"Architecture": "formalisms/Architecture"}, "models/revise_architecture")
         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", "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_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({"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())
         #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([])
         executed = set([])
 
 
         def get_function(filename, fixed=None):
         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!")
                     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:
                 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
             return func
 
 
         cb_req = get_function("models/requirements_model.mvc")
         cb_req = get_function("models/requirements_model.mvc")
         cb_plant = get_function("models/plant_model.mvc")
         cb_plant = get_function("models/plant_model.mvc")
         cb_env = get_function("models/environment_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_query = get_function("models/query_model.mvc")
         cb_arch = get_function("models/architecture_model.mvc")
         cb_arch = get_function("models/architecture_model.mvc")
 
 
@@ -198,6 +217,9 @@ class TestPowerWindow(unittest.TestCase):
 
 
         thrd.join()
         thrd.join()
 
 
+        called = len(os.listdir(".TEST_POWER_WINDOW"))
+        shutil.rmtree(".TEST_POWER_WINDOW")
+
         if called != 11:
         if called != 11:
             print(called)
             print(called)
             raise Exception("Not executed sufficiently:" + str(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 MatPlotLibWrapper import *
 from plot import *
 from plot import *
 from modelverse import *
 from modelverse import *
+import uuid
 
 
 # Login to Modelverse and have model rendered
 # Login to Modelverse and have model rendered
 init()
 init()
 login("admin", "admin")
 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"
 # Convert rendered to "MatPlotLib JSON"
-data = json.loads(rendered)
+data = rendered
 plots = {}
 plots = {}
 axis = {}
 axis = {}
 datasets = {}
 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)
     raise PrimitiveFinished(result)
 
 
 def reverseKeyLookup(a, b, **remainder):
 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])]
     edges_out, edges_in = yield [("RO", [a]), ("RI", [b])]
     options = set(edges_out) & set(edges_in)
     options = set(edges_out) & set(edges_in)
     if options:
     if options:

+ 8 - 8
kernel/modelverse_kernel/main.py

@@ -293,10 +293,6 @@ class ModelverseKernel(object):
             print("Got roots: " + str(evalstack_roots))
             print("Got roots: " + str(evalstack_roots))
             raise Exception("Could not process continue statement!")
             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 = \
         prev_evalstack_roots, old_evalstack_phase_link = \
                             yield [("RRD", [evalstack_root, "prev"]),
                             yield [("RRD", [evalstack_root, "prev"]),
                                    ("RDE", [evalstack_root, "phase"]),
                                    ("RDE", [evalstack_root, "phase"]),
@@ -496,18 +492,22 @@ class ModelverseKernel(object):
                                    ("CNV", ["init"]),
                                    ("CNV", ["init"]),
                                    ("CNV", ["init"]),
                                    ("CNV", ["init"]),
                                   ]
                                   ]
-            _, _, _, _, _, _, _, _, _, _ = \
+            _, _, _, _, _, _, _, _, _ = \
                             yield [("CD", [task_frame, "IP", body]),
                             yield [("CD", [task_frame, "IP", body]),
                                    ("CD", [task_frame, "phase", new_phase]),
                                    ("CD", [task_frame, "phase", new_phase]),
                                    ("CD", [task_frame, "evalstack", new_evalstack]),
                                    ("CD", [task_frame, "evalstack", new_evalstack]),
                                    ("CD", [new_evalstack, "prev", evalstack]),
                                    ("CD", [new_evalstack, "prev", evalstack]),
                                    ("CD", [evalstack, "inst", inst]),
                                    ("CD", [evalstack, "inst", inst]),
-                                   ("CD", [evalstack, self.taskname, inst]),
                                    ("CD", [evalstack, "phase", evalstack_phase]),
                                    ("CD", [evalstack, "phase", evalstack_phase]),
                                    ("DE", [evalstack_link]),
                                    ("DE", [evalstack_link]),
                                    ("DE", [ip_link]),
                                    ("DE", [ip_link]),
                                    ("DE", [phase_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:
         else:
             phase_link, new_phase = \
             phase_link, new_phase = \
                             yield [("RDE", [task_frame, "phase"]),
                             yield [("RDE", [task_frame, "phase"]),
@@ -702,6 +702,7 @@ class ModelverseKernel(object):
             if prev_frame is None:
             if prev_frame is None:
                 _, =            yield [("DN", [task_root])]
                 _, =            yield [("DN", [task_root])]
                 del self.debug_info[self.taskname]
                 del self.debug_info[self.taskname]
+                print("Cleanup task " + str(self.taskname))
             else:
             else:
                 if self.debug_info[self.taskname]:
                 if self.debug_info[self.taskname]:
                     self.debug_info[self.taskname].pop()
                     self.debug_info[self.taskname].pop()
@@ -906,8 +907,7 @@ class ModelverseKernel(object):
                                    ("RD", [inst, "last_param"]),
                                    ("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, =         yield [("RD", [last_param, "name"])]
             name_value, =   yield [("RV", [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])]
     b_value, = yield [("RV", [b])]
     edge, = yield [("RDE", [a, b_value])]
     edge, = yield [("RDE", [a, b_value])]
     if edge is None:
     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 [("RDK", [a])]
         keys = yield [("RV", [i]) for i in keys]
         keys = yield [("RV", [i]) for i in keys]
         print("Keys: " + str(keys))
         print("Keys: " + str(keys))

+ 3 - 0
kernel/modelverse_kernel/request_handler.py

@@ -278,6 +278,9 @@ class RequestHandler(object):
         except primitive_functions.PrimitiveFinished:
         except primitive_functions.PrimitiveFinished:
             self.push_generator(None)
             self.push_generator(None)
             raise
             raise
+        except:
+            print("EXCEPTION for " + str(locals()))
+            raise
 
 
     def execute_call_args(self, request_args):
     def execute_call_args(self, request_args):
         """Executes a CALL_ARGS-request with the given argument list."""
         """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()
 init()
 login("admin", "admin")
 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("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())
 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())
 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/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/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_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/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/Query", "formalisms/SimpleClassDiagrams", open("models/query.mvc", 'r').read())
 model_add("formalisms/Architecture", "formalisms/SimpleClassDiagrams", open("models/architecture.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/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/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", "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/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/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/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"}, {"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_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", "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", "Query": "formalisms/Query"}, {"Query": "formalisms/Query"}, "models/revise_query")
 transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Architecture": "formalisms/Architecture"}, {"Architecture": "formalisms/Architecture"}, "models/revise_architecture")
 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", "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_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({"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)
 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 = subprocess.Popen(program_to_execute)
 
 
     server.wait()
     server.wait()
+
 finally:
 finally:
     # Stop the server
     # Stop the server
     try:
     try:

+ 0 - 1
services/HUTN/main.py

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

+ 0 - 1
services/JSON/main.py

@@ -5,7 +5,6 @@ import os
 import json
 import json
 
 
 import time
 import time
-time.sleep(1)
 init(sys.argv[1])
 init(sys.argv[1])
 login("JSON", "JSON")
 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)
 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 author: Yentl Van Tendeloo
 Model name:   Logging
 Model name:   Logging
@@ -65,6 +65,7 @@ class Logging(RuntimeClassBase):
         _init_1.setTrigger(Event("_0after"))
         _init_1.setTrigger(Event("_0after"))
         self.states["/init"].addTransition(_init_1)
         self.states["/init"].addTransition(_init_1)
         _init_2 = Transition(self, self.states["/init"], [self.states["/finished"]])
         _init_2 = Transition(self, self.states["/init"], [self.states["/finished"]])
+        _init_2.setAction(self._init_2_exec)
         _init_2.setTrigger(Event("terminate", "inp"))
         _init_2.setTrigger(Event("terminate", "inp"))
         self.states["/init"].addTransition(_init_2)
         self.states["/init"].addTransition(_init_2)
     
     
@@ -77,6 +78,10 @@ class Logging(RuntimeClassBase):
     def _init_0_exec(self, parameters):
     def _init_0_exec(self, parameters):
         value = parameters[0]
         value = parameters[0]
         self.log.append(value)
         self.log.append(value)
+        print("Logging " + str(value))
+    
+    def _init_2_exec(self, parameters):
+        print("Got terminate")
     
     
     def initializeStatechart(self):
     def initializeStatechart(self):
         # enter default state
         # enter default state
@@ -102,4 +107,5 @@ class Controller(ThreadsControllerBase):
         ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
         ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
         self.addInputPort("inp")
         self.addInputPort("inp")
         self.addOutputPort("outp")
         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
                 self.log = log
             </body>
             </body>
         </constructor>
         </constructor>
+
         <scxml initial="init">
         <scxml initial="init">
             <state id="init">
             <state id="init">
                 <transition event="input" port="inp" target=".">
                 <transition event="input" port="inp" target=".">
                     <parameter name="value"/>
                     <parameter name="value"/>
                     <script>
                     <script>
+                        print("Logging " + str(value))
                         self.log.append(value)
                         self.log.append(value)
                     </script>
                     </script>
                 </transition>
                 </transition>
 
 
                 <transition after="0.1" target="."/>
                 <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>
 
 
             <state id="finished"/>
             <state id="finished"/>

+ 40 - 37
unit/test_all.py

@@ -173,38 +173,38 @@ class TestModelverse(unittest.TestCase):
     def test_operations(self):
     def test_operations(self):
         log = []
         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", "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/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())
         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_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)
         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.daemon = True
         thrd.start()
         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")
         print("Joining")
         thrd.join()
         thrd.join()
         print("Joined")
         print("Joined")
+        print("Got log: " + str(log))
         assert set(log) == set(['"p1" --> 1',
         assert set(log) == set(['"p1" --> 1',
                                 '"p2" --> 2',
                                 '"p2" --> 2',
                                 '"p3" --> 3'])
                                 '"p3" --> 3'])
@@ -239,7 +240,7 @@ class TestModelverse(unittest.TestCase):
         thrd.daemon = True
         thrd.daemon = True
         thrd.start()
         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()
         thrd.join()
         assert set(log) == set(['"p1" --> 0',
         assert set(log) == set(['"p1" --> 0',
                                 '"p2" --> 1',
                                 '"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_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())
         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
         import log_output
         log = []
         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/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())
         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)
         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")
         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():
 def start_mvc():
     port = getFreePort()
     port = getFreePort()
     address = "127.0.0.1:%s" % port
     address = "127.0.0.1:%s" % port
+    print("Execute run local MV")
     proc = execute("run_local_modelverse", [str(port)], wait=False)
     proc = execute("run_local_modelverse", [str(port)], wait=False)
     return proc, address
     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
     ID += 1
     return ID
     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:
     while 1:
         empty = True
         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
             empty = False
 
 
-        # Fetch output from the SC
         response = port_sc.fetch(0)
         response = port_sc.fetch(0)
         if response is not None:
         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
             empty = False
 
 
         if empty:
         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):
 def INPUT(action, context, parameters):
     controller.addInput(Event("action", "action_in", [action, _next_ID(), 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))
                 print("Unknown error: " + str(response.parameters))
                 raise UnknownError()
                 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 controller
     global ID
     global ID
     global responses
     global responses
 
 
-    controller = modelverse_SCCD.Controller()
+    controller = modelverse_SCCD.Controller(taskname)
     socket2event.boot_translation_service(controller)
     socket2event.boot_translation_service(controller)
 
 
     ID = 0
     ID = 0
@@ -124,6 +189,8 @@ def init(address_param="127.0.0.1:8001", timeout=20.0):
     return OUTPUT()
     return OUTPUT()
 
 
 def login(username, password):
 def login(username, password):
+    controller.username = username
+    controller.password = password
     INPUT("login", None, [username, password])
     INPUT("login", None, [username, password])
     return OUTPUT()
     return OUTPUT()
 
 
@@ -147,8 +214,8 @@ def verify(model_name, metamodel_name):
     INPUT("verify", None, [model_name, metamodel_name])
     INPUT("verify", None, [model_name, metamodel_name])
     return OUTPUT()
     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()
     return OUTPUT()
 
 
 def disconnect():
 def disconnect():
@@ -173,31 +240,35 @@ def transformation_between(sources, targets):
 
 
 def transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=None):
 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])
     INPUT("transformation_add_MT", None, [source_metamodels, target_metamodels, operation_name, code, True])
-    context, model = OUTPUT()
+    model = OUTPUT()
+
     if callback is not None:
     if callback is not None:
-        callback(context)
-    INPUT("exit", context, [])
+        __invoke(callback, model)
+    controller.addInput(Event("data_input", "action_in", [None, None]))
+
     return OUTPUT()
     return OUTPUT()
 
 
 def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=None):
 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])
     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()
     return OUTPUT()
 
 
 def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=None):
 def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=None):
     INPUT("transformation_add_MANUAL", None, [source_metamodels, target_metamodels, operation_name, True])
     INPUT("transformation_add_MANUAL", None, [source_metamodels, target_metamodels, operation_name, True])
-    context, model = OUTPUT()
+    model = OUTPUT()
+
     if callback is not None:
     if callback is not None:
-        callback(context)
-    INPUT("exit", context, [])
+        __invoke(callback, model)
+    controller.addInput(Event("data_input", "action_in", [None, None]))
+
     return OUTPUT()
     return OUTPUT()
 
 
 def __transformation_execute(operation_name, input_models_dict, output_models_dict, statechart, tracability_model, fetch_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])
         port_sc = statechart[0].addOutputListener(statechart[2])
 
 
     INPUT("transformation_execute", None, [operation_name, input_models_dict, output_models_dict, tracability_model, fetch_output])
     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:
     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):
 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)
     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=""):
 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])
     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()
     return OUTPUT()
 
 
 def transformation_signature(operation_name):
 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])
     INPUT("all_instances", context, [model_name, type_name])
     return OUTPUT()
     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
     # for all callbacks to SCs, start up the output port already
     sc_ports = {}
     sc_ports = {}
-
     for k, v in callbacks.items():
     for k, v in callbacks.items():
         if isinstance(v, (tuple, list)):
         if isinstance(v, (tuple, list)):
             # Is a statechart, so register already
             # Is a statechart, so register already
             sc_ports[k] = v[0].addOutputListener(v[2])
             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:
     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:
         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():
 def get_taskname():
     """Fetch the taskname of the current connection."""
     """Fetch the taskname of the current connection."""
     return controller.taskname
     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!"""
 """ Some hardcoded functions... Way easier to express them with code than with statecharts!"""
 import json
 import json
 import urllib
 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">
             <state id="finished">
                 <script>
                 <script>
-                    print("FINISHED")
+                    print("FINISHED SC")
                 </script>
                 </script>
             </state>
             </state>
         </scxml>
         </scxml>