Browse Source

Added first attempt at exit/entry actions

Yentl Van Tendeloo 8 years ago
parent
commit
86fcd60334
1 changed files with 196 additions and 8 deletions
  1. 196 8
      models/SCCD_execute.alc

+ 196 - 8
models/SCCD_execute.alc

@@ -52,7 +52,39 @@ Element function filter_exists(model : Element, set : Element, attribute_name :
 
 	return result!
 
-Element function expand_state(model : Element, state : String):
+Element function filter_resolve_exists(model : Element, set : Element, attribute_name : String):
+	Element keys
+	String key
+	Element result
+
+	result = create_node()
+	while (read_nr_out(set) > 0):
+		key = set_pop(set)
+		if (element_neq(read_attribute(model, key, attribute_name), read_root())):
+			set_add(result, read_attribute(model, key, attribute_name))
+
+	return result!
+
+Element function expand_current_state(model : Element, state : String, current_states : Element):
+	// Find the hierarchy of all current states, and select those that contain the currently selected state
+	Element result
+	result = create_node()
+	current_states = set_copy(current_states)
+
+	Element hierarchy
+	String deep_state
+	while (read_nr_out(current_states) > 0):
+		deep_state = set_pop(current_states)
+		hierarchy = find_hierarchy(model, deep_state)
+		// Got the hierarchy of one of the states
+
+		if (set_in(hierarchy, state)):
+			// This hierarchy contains the root state we are checking for, so add to set
+			set_add(result, deep_state)
+
+	return result!
+
+Element function expand_initial_state(model : Element, state : String):
 	String t
 	t = read_type(model, state)
 	if (t == "SCCD/CompositeState"):
@@ -76,11 +108,11 @@ Element function expand_composite_state(model : Element, composite_state : Strin
 	initial = set_pop(filter(model, allAssociationDestinations(model, composite_state, "SCCD/composite_children"), "isInitial", True))
 
 	// Expand the initial state, depending on what it is
-	return expand_state(model, initial)!
+	return expand_initial_state(model, initial)!
 
 Element function expand_parallel_state(model : Element, parallel_state : String):
 	// Resolve all initial states from a single parallel state
-    Element children
+	Element children
 	Element result
 	Element expanded_children
 
@@ -88,7 +120,7 @@ Element function expand_parallel_state(model : Element, parallel_state : String)
 	result = create_node()
 
 	while (read_nr_out(children) > 0):
-		set_merge(result, expand_state(model, set_pop(children)))
+		set_merge(result, expand_initial_state(model, set_pop(children)))
 
 	return result!
 
@@ -106,7 +138,7 @@ Void function start_class(model : Element, data : Element, class : String):
 	String initial_state
 	// Should only be one behaviour linked to it!
 	initial_state = set_pop(allAssociationDestinations(model, class, "SCCD/behaviour"))
-	dict_add(class_handle, "states", expand_state(model, initial_state))
+	dict_add(class_handle, "states", expand_initial_state(model, initial_state))
 
 	// Add all attributes
 	Element attributes
@@ -183,8 +215,22 @@ Element function execute_transition(model : Element, data : Element, class : Str
 		event = set_pop(events)
 		set_add(data["events"], read_attribute(model, event, "event"))
 
-	// Return new set of states
-	return expand_state(model, readAssociationDestination(model, transition))!
+	// Find new set of states
+	Element target_states
+	Element source_states
+	source_states = expand_current_state(model, readAssociationSource(model, transition), data["classes"][class]["states"])
+	target_states = expand_initial_state(model, readAssociationDestination(model, transition))
+
+	// Do all required operations 
+	Element actions
+	actions = get_actions_to_execute(model, source_states, target_states)
+	while (read_nr_out(actions) > 0):
+		Element action
+		action = list_pop(actions, 0)
+		action = get_func_AL_model(import_node(action))
+		action(data["attributes"])
+
+	return target_states!
 
 Boolean function step_class(model : Element, data : Element, class : String):
 	// Find enabled transitions in a class and execute it, updating the state
@@ -241,7 +287,7 @@ Void function reschedule_timeouts(model : Element, data : Element, class : Strin
 		state = set_pop(states)
 
 		// NOTE this set_merge does not eliminate duplicates, though this should happen later on when adding the timer (see other NOTE)
-		timed_transitions = set_merge(timed_transitions, filter_exists(model, allOutgoingAssociationInstances(model, state, "SCCD/transition"), "after"))
+		set_merge(timed_transitions, filter_exists(model, allOutgoingAssociationInstances(model, state, "SCCD/transition"), "after"))
 
 	// Remove timers that no longer exist
 	old_timed_transitions = dict_keys(data["classes"][class]["timers"])
@@ -270,6 +316,148 @@ Void function reschedule_timeouts(model : Element, data : Element, class : Strin
 
 	return !
 
+String function get_parent(model : Element, state : String):
+	Element tmp_set
+	tmp_set = allAssociationOrigins(model, state, "composite_children")
+	set_merge(tmp_set, allAssociationOrigins(model, state, "parallel_children"))
+	if (read_nr_out(tmp_set) > 0):
+		return set_pop(tmp_set)!
+	else:
+		return ""!
+
+Element function find_hierarchy(model : Element, state : String):
+	Element result
+	String parent
+
+	parent = get_parent(model, state)
+	if (parent == ""):
+		// Are at the topmost element, so empty hierarchy for us
+		result = create_node()
+	else:
+		// We have a parent, so take the parent list first
+		result = find_hierarchy(model, parent)
+
+	// Now append ourself
+	list_append(result, state)
+	return result!
+
+Element function get_actions_to_execute(model : Element, source_states : Element, target_states : Element):
+	Element result
+	result = create_node()
+
+	source_states = set_copy(source_states)
+	target_states = set_copy(target_states)
+
+	// Add all exit and entry actions to the list of actions to execute
+	// Do this by finding the common parent, and then doing all exit actions up to that node, and all entry actions up to the target_state
+
+	// First, find the hierarchy!
+	Element hierarchy_sources
+	Element hierarchy_targets
+	Element all_hierarchies
+
+	hierarchy_sources = create_node()
+	while (read_nr_out(source_states) > 0):
+		set_add(hierarchy_sources, find_hierarchy(model, set_pop(source_states)))
+
+	hierarchy_targets = create_node()
+	while (read_nr_out(target_states) > 0):
+		set_add(hierarchy_targets, find_hierarchy(model, set_pop(target_states)))
+
+	all_hierarchies = set_copy(hierarchy_sources)
+	set_merge(all_hierarchies, hierarchy_targets)
+
+	// Difference these all lists, finding the first common entry
+	Element iter_hierarchies
+	Integer i
+	String current
+	Element hierarchy
+	i = 0
+	while (True):
+		// Check the i-th element in both and see if they are equal
+		current = ""
+		iter_hierarchies = set_copy(all_hierarchies)
+		while (read_nr_out(iter_hierarchies) > 0):
+			hierarchy = set_pop(iter_hierarchies)
+
+			// Exhausted one of the lists
+			if (i == list_len(hierarchy)):
+				break!
+
+			// First entry, so read out value as reference
+			if (current == ""):
+				current = list_read(hierarchy, i)
+
+			// Check with reference element
+			if (bool_not(value_eq(list_read(hierarchy, i), current))):
+				break!
+
+		// i-th element equal for all hierarchies, so go to next element
+		i = i + 1
+
+	// Found the first differing element at position i
+
+	// All elements remaining in hierarchy_source are to be traversed in REVERSE order for the exit actions
+	// All elements remaining in hierarchy_target are to be traversed in NORMAL order for the entry actions
+	// This is not that simple either, as we need to consider that some actions might already have been added to the list...
+
+	// Add hierarchy_sources actions
+	String state
+	Element visited
+	Element action
+	Element spliced_hierarchy
+	Element hierarchy_source
+	Element hierarchy_target
+	visited = create_node()
+	while (read_nr_out(hierarchy_sources) > 0):
+		// Get one of these hierarchies
+		hierarchy_source = set_pop(hierarchy_sources)
+		spliced_hierarchy = list_splice(hierarchy_source, i, list_len(hierarchy_source))
+		while (list_len(spliced_hierarchy) > 0):
+			state = list_pop(spliced_hierarchy, list_len(spliced_hierarchy) - 1)
+			if (set_in(visited, state)):
+				// Already added this state, so don't bother
+				continue!
+			else:
+				// New state, so prepend it to the list
+				// Prepend, instead of append, as we want to do these operations in reverse order!
+
+				// First check if there is any exit action at all
+				action = read_attribute(model, state, "exit_action")
+				if (element_neq(action, read_root())):
+					// An exit action is found!
+					list_insert(result, 0, get_func_AL_model(import_node(action)))
+
+				// Add this state as visited, even though there might not have been an associated action
+				set_add(visited, state)
+
+	// Add hierarchy_targets actions
+	// Clear visited, just to be safe, though it should not matter
+	visited = create_node()
+	while (read_nr_out(hierarchy_targets) > 0):
+		// Get one of these hierarchies
+		hierarchy_target = set_pop(hierarchy_targets)
+		spliced_hierarchy = list_splice(hierarchy_target, i, list_len(hierarchy_target))
+		while (list_len(spliced_hierarchy) > 0):
+			state = list_pop(spliced_hierarchy, list_len(spliced_hierarchy) - 1)
+			if (set_in(visited, state)):
+				// Already added this state, so don't bother
+				continue!
+			else:
+				// New state, so append it to the list
+				// Append, instead of prepend, as we want to do these operations in normal order!
+
+				// First check if there is any entry action at all
+				action = read_attribute(model, state, "entry_action")
+				if (element_neq(action, read_root())):
+					// An entry action is found!
+					list_append(result, get_func_AL_model(import_node(action)))
+
+				// Add this state as visited, even though there might not have been an associated action
+				set_add(visited, state)
+
+	return result!
+
 Float function step(model : Element, data : Element):
 	// Step through all classes
 	Element classes