Browse Source

Bugfixes to SCCD

Yentl Van Tendeloo 8 years ago
parent
commit
cfbf73d817
3 changed files with 167 additions and 72 deletions
  1. 6 6
      models/SCCD.mvc
  2. 154 59
      models/SCCD_execute.alc
  3. 7 7
      models/dynamic_trafficlight.mvc

+ 6 - 6
models/SCCD.mvc

@@ -57,6 +57,12 @@ Class BasicState : State{
     onExitScript? : Action
 }
 
+Class Raise{
+    event : String
+    scope? : String
+    target? : String
+    parameter? : Action
+}
 Association onEntryRaise (BasicState, Raise) {}
 Association onExitRaise (BasicState, Raise) {}
 
@@ -65,12 +71,6 @@ Association behaviour(Class, BasicState){
     target_upper_cardinality = 1
 }
 
-Class Raise{
-    event : String
-    scope? : String
-    target? : String
-    parameter? : Action
-}
 Association state_onentry_raises(BasicState, Raise){
     order : Natural
 }

+ 154 - 59
models/SCCD_execute.alc

@@ -16,13 +16,13 @@ Void function print_states(model : Element, data : Element):
 	while (read_nr_out(classes) > 0):
 		class = set_pop(classes)
 		log(string_join(string_join(string_join("  ", class["ID"]), " : "), read_attribute(model, class["type"], "name")))
-		log("    Attributes: " + dict_to_string(class["attributes"]))
+		log("	Attributes: " + dict_to_string(class["attributes"]))
 
 		states = set_copy(class["states"])
-		log("    States:")
+		log("	States:")
 		while (read_nr_out(states) > 0):
 			state = set_pop(states)
-			log(string_join("        ", read_attribute(model, state, "name")))
+			log(string_join("		", read_attribute(model, state, "name")))
 
 	return!
 
@@ -171,7 +171,7 @@ Void function start_class(model : Element, data : Element, class : String, ident
 	set_add(init, "")
 	// Initial state before initialization is the set with an empty hierarchy
 	// Empty set would not find any difference between the source and target
-	execute_actions(model, init, set_copy(class_handle["states"]), attributes)
+	execute_actions(model, init, set_copy(class_handle["states"]), attributes, data, "")
 
 	dict_add(data["classes"], class_handle["ID"], class_handle)
 
@@ -260,15 +260,6 @@ Element function get_enabled_transitions(model : Element, state : String, data :
 
 	return result!
 
-Void function execute_actions(model : Element, source_states : Element, target_states : Element, class_data : Element):
-	Element actions
-	actions = get_actions_to_execute(model, source_states, target_states, class_data)
-	while (read_nr_out(actions) > 0):
-		Element action
-		action = list_pop(actions, 0)
-		action(class_data["attributes"])
-	return!
-
 Void function process_raised_event(model : Element, event : Element, parameter_action : Element, data : Element):
 	String scope
 	scope = read_attribute(model, event, "scope")
@@ -337,7 +328,7 @@ Element function execute_transition(model : Element, data : Element, class : Str
 	source_states = expand_current_state(model, readAssociationSource(model, transition), data["classes"][class])
 	target_states = expand_initial_state(model, readAssociationDestination(model, transition), data["classes"][class])
 
-	execute_actions(model, source_states, target_states, data["classes"][class])
+	execute_actions(model, source_states, target_states, data["classes"][class], data, readAssociationSource(model, transition))
 
 	return target_states!
 
@@ -372,6 +363,7 @@ Boolean function step_class(model : Element, data : Element, class : String):
 			if (read_nr_out(transitions) > 0):
 				// Found an enabled transition, so store that one
 				transition = random_choice(transitions)
+				log("Execute enabled transition: " + cast_v2s(read_attribute(model, list_read(transition, 0), "name")))
 				
 				// Execute transition
 				set_merge(new_states, execute_transition(model, data, class, transition))
@@ -391,7 +383,7 @@ Boolean function step_class(model : Element, data : Element, class : String):
 			
 	// Update states
 	dict_overwrite(data["classes"][class], "states", new_states)
-	reschedule_timeouts(model, data, class)
+	//reschedule_timeouts(model, data, class)
 
 	return transitioned!
 
@@ -404,6 +396,7 @@ Void function reschedule_timeouts(model : Element, data : Element, class : Strin
 
 	timed_transitions = create_node()
 	states = set_copy(data["classes"][class]["states"])
+	log("Rescheduling timeouts")
 
 	// Collect all timed transitions that are currently active
 	while (read_nr_out(states) > 0):
@@ -414,16 +407,23 @@ Void function reschedule_timeouts(model : Element, data : Element, class : Strin
 
 	// Remove timers that no longer exist
 	old_timed_transitions = dict_keys(data["classes"][class]["timers"])
+
+	log("Got timed transitions: " + set_to_string(timed_transitions))
+	log("Got currently scheduled transitions: " + set_to_string(old_timed_transitions))
+
 	while (read_nr_out(old_timed_transitions) > 0):
 		transition = set_pop(old_timed_transitions)
+		log("Check remove " + cast_v2s(transition))
 
 		if (bool_not(set_in(timed_transitions, transition))):
 			// Transition is no longer scheduled for any state, so remove
 			dict_delete(data["classes"][class]["timers"], transition)
+			log("Remove timer for transition " + cast_v2s(read_attribute(model, transition, "name")))
 
 	// Schedule timers that are not already scheduled
 	while (read_nr_out(timed_transitions) > 0):
 		transition = set_pop(timed_transitions)
+		log("Check add " + cast_v2s(transition))
 
 		// NOTE Normally, a timer will not be added twice here, as the previous occurence will already find it
 		if (bool_not(dict_in(data["classes"][class]["timers"], transition))):
@@ -436,6 +436,8 @@ Void function reschedule_timeouts(model : Element, data : Element, class : Strin
 			after_duration = after(data["classes"][class]["attributes"])
 
 			dict_add(data["classes"][class]["timers"], transition, float_addition(data["start_time"], after_duration))
+			log("Add timer for transition " + cast_v2s(read_attribute(model, transition, "name")))
+	log("All done!")
 
 	return !
 
@@ -461,9 +463,11 @@ Element function find_hierarchy(model : Element, state : String):
 		list_append(result, state)
 		return result!
 
-Element function get_actions_to_execute(model : Element, source_states : Element, target_states : Element, class_data : Element):
-	Element result
-	result = create_node()
+Void function execute_actions(model : Element, source_states : Element, target_states : Element, class_data : Element, data : Element, transition_source : String):
+	Element exit
+	Element entry
+	exit = create_node()
+	entry = create_node()
 
 	source_states = set_copy(source_states)
 	target_states = set_copy(target_states)
@@ -493,32 +497,54 @@ Element function get_actions_to_execute(model : Element, source_states : Element
 	String current
 	Element hierarchy
 	Boolean finished
-	i = 0
+	Integer transition_depth
+	Integer lca_depth
+	lca_depth = 0
 	finished = False
-	while (bool_not(finished)):
-		// 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)):
-				finished = True
-				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))):
-				finished = True
-				break!
+	if (transition_source != ""):
+		// Find out the level of the transition_source by fetching its hierarchy
+		transition_depth = list_len(find_hierarchy(model, transition_source)) - 1
+
+		// Now check for the least common ancestor
+		while (bool_not(finished)):
+			// 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 (lca_depth >= list_len(hierarchy)):
+					finished = True
+					break!
+
+				// Reached the same level as transition depth already, so no need to increase
+				if (lca_depth == transition_depth):
+					finished = True
+					break!
+
+				// First entry, so read out value as reference
+				if (current == ""):
+					current = list_read(hierarchy, lca_depth)
+
+				// Check with reference element
+				if (bool_not(value_eq(list_read(hierarchy, lca_depth), current))):
+					finished = True
+					break!
+
+			// i-th element equal for all hierarchies, so go to next element
+			if (bool_not(finished)):
+				lca_depth = lca_depth + 1
+
+		if (lca_depth < transition_depth):
+			i = lca_depth
+		else:
+			i = transition_depth
 
-		// i-th element equal for all hierarchies, so go to next element
-		if (bool_not(finished)):
-			i = i + 1
+	else:
+		// Initial, so just set i to zero
+		i = 0
 
 	// Found the first differing element at position i
 
@@ -546,20 +572,11 @@ Element function get_actions_to_execute(model : Element, source_states : Element
 			else:
 				// New state, so prepend it to the list
 				// Prepend, instead of append, as we want to do these operations in reverse order!
+				list_insert(exit, state, 0)
 
-				// First check if there is any exit action at all
-				action = read_attribute(model, state, "onExitScript")
-				if (element_neq(action, read_root())):
-					// An exit action is found!
-					list_insert(result, get_func_AL_model(import_node(action)), 0)
-
-				// Add this state as visited, even though there might not have been an associated action
+				// Add this state as visited
 				set_add(visited, state)
 
-				// Also update the history of this state, which will be important if we have a history state later on
-				if (read_type(model, state) == "SCCD/CompositeState"):
-					dict_overwrite(class_data["history"], state, expand_current_state(model, state, class_data))
-
 	// Add hierarchy_targets actions
 	// Clear visited, just to be safe, though it should not matter
 	visited = create_node()
@@ -575,17 +592,93 @@ Element function get_actions_to_execute(model : Element, source_states : Element
 			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, "onEntryScript")
-				if (element_neq(action, read_root())):
-					// An entry action is found!
-					list_append(result, get_func_AL_model(import_node(action)))
+				list_append(entry, state)
 
 				// Add this state as visited, even though there might not have been an associated action
 				set_add(visited, state)
 
-	return result!
+	// Now we have a list of traversed states!
+	// Start executing all their operations in order
+
+	Element events
+	String event
+	// First do exit actions
+	while (read_nr_out(exit) > 0):
+		state = list_pop(exit, 0)
+		log("EXIT " + cast_v2s(read_attribute(model, state, "name")))
+
+		// Set history when leaving
+		if (read_type(model, state) == "SCCD/CompositeState"):
+			dict_overwrite(class_data["history"], state, expand_current_state(model, state, class_data))
+
+		// Do exit actions
+		action = read_attribute(model, state, "onExitScript")
+		if (element_neq(action, read_root())):
+			// Got a script, so execute!
+			action = get_func_AL_model(import_node(action))
+			action(class_data["attributes"])
+
+		// Raise events
+		events = allAssociationDestinations(model, state, "SCCD/onExitRaise")
+		while (read_nr_out(events) > 0):
+			event = set_pop(events)
+
+			Element parameter_action
+			parameter_action = read_attribute(model, event, "parameter")
+			if (element_neq(parameter_action, read_root())):
+				// Got a parameter to evaluate
+				parameter_action = get_func_AL_model(import_node(parameter_action))
+				parameter_action = parameter_action(class_data["attributes"])
+
+			process_raised_event(model, event, parameter_action, data)
+
+		// Unschedule after events
+		Element timed_transitions
+		timed_transitions = filter_exists(model, allOutgoingAssociationInstances(model, state, "SCCD/transition"), "after")
+		log("Checking for unschedule of timed transitions: " + set_to_string(timed_transitions))
+		while (read_nr_out(timed_transitions) > 0):
+			log("Unschedule timer")
+			dict_delete(class_data["timers"], set_pop(timed_transitions))
+
+	// Then do entry actions
+	while (read_nr_out(entry) > 0):
+		state = list_pop(entry, 0)
+		log("ENTRY " + cast_v2s(read_attribute(model, state, "name")))
+
+		// Do entry actions
+		action = read_attribute(model, state, "onEntryScript")
+		if (element_neq(action, read_root())):
+			// Got a script, so execute!
+			action = get_func_AL_model(import_node(action))
+			action(class_data["attributes"])
+
+		// Raise events
+		events = allAssociationDestinations(model, state, "SCCD/onEntryRaise")
+		while (read_nr_out(events) > 0):
+			event = set_pop(events)
+
+			Element parameter_action
+			parameter_action = read_attribute(model, event, "parameter")
+			if (element_neq(parameter_action, read_root())):
+				// Got a parameter to evaluate
+				parameter_action = get_func_AL_model(import_node(parameter_action))
+				parameter_action = parameter_action(class_data["attributes"])
+
+			process_raised_event(model, event, parameter_action, data)
+
+		// Schedule after events
+		Element timed_transitions
+		String transition
+		Element after
+		timed_transitions = filter_exists(model, allOutgoingAssociationInstances(model, state, "SCCD/transition"), "after")
+		log("Checking for schedule of timed transitions: " + set_to_string(timed_transitions))
+		while (read_nr_out(timed_transitions) > 0):
+			log("Schedule timer")
+			transition = set_pop(timed_transitions)
+			after = get_func_AL_model(import_node(read_attribute(model, transition, "after")))
+			dict_add(class_data["timers"], transition, float_addition(data["start_time"], after(class_data["attributes"])))
+
+	return !
 
 Float function step(model : Element, data : Element):
 	// Step through all classes
@@ -620,8 +713,10 @@ Float function step(model : Element, data : Element):
 
 	if (transitioned):
 		// Do another step, as we can transition
+		log("Transitioned, do another step!")
 		return 0.0!
 	else:
+		log("Idle!")
 		return float_subtraction(t_min, data["start_time"])!
 
 Boolean function main(model : Element):

+ 7 - 7
models/dynamic_trafficlight.mvc

@@ -271,7 +271,7 @@ transition (trafficlight_main_main_trafficlight_interrupted, trafficlight_main_m
 transition (trafficlight_main_main_trafficlight_normal_red, trafficlight_main_main_trafficlight_normal_green) {
     name = "after 60s"
     after = $
-            Float function after(attributes : Element, parameters : Element):
+            Float function after(attributes : Element):
                 return 60.0!
         $
 }
@@ -279,7 +279,7 @@ transition (trafficlight_main_main_trafficlight_normal_red, trafficlight_main_ma
 transition (trafficlight_main_main_trafficlight_normal_green, trafficlight_main_main_trafficlight_normal_yellow) {
     name = "after 55s"
     after = $
-            Float function after(attributes : Element, parameters : Element):
+            Float function after(attributes : Element):
                 return 55.0!
         $
 }
@@ -287,7 +287,7 @@ transition (trafficlight_main_main_trafficlight_normal_green, trafficlight_main_
 transition (trafficlight_main_main_trafficlight_normal_yellow, trafficlight_main_main_trafficlight_normal_red) {
     name = "after 5s"
     after = $
-            Float function after(attributes : Element, parameters : Element):
+            Float function after(attributes : Element):
                 return 5.0!
         $
 
@@ -299,7 +299,7 @@ transition (trafficlight_main_main_trafficlight_normal_yellow, trafficlight_main
 transition (trafficlight_main_main_trafficlight_interrupted_yellow, trafficlight_main_main_trafficlight_interrupted_black) {
     name = "after 500ms"
     after = $
-            Float function after(attributes : Element, parameters : Element):
+            Float function after(attributes : Element):
                 return 0.5!
         $
 }
@@ -307,7 +307,7 @@ transition (trafficlight_main_main_trafficlight_interrupted_yellow, trafficlight
 transition (trafficlight_main_main_trafficlight_interrupted_black, trafficlight_main_main_trafficlight_interrupted_yellow) {
     name = "after 500ms"
     after = $
-            Float function after(attributes : Element, parameters : Element):
+            Float function after(attributes : Element):
                 return 0.5!
         $
 }
@@ -362,7 +362,7 @@ transition (trafficlight_main_main_timer_running_decidingcolor, trafficlight_mai
 transition (trafficlight_main_main_timer_running_red, trafficlight_main_main_timer_running_red) {
     name = "after 1s / counter -= 1"
     after = $
-            Float function after(attributes : Element, parameters : Element):
+            Float function after(attributes : Element):
                 return 1.0!
         $
     script = $
@@ -375,7 +375,7 @@ transition (trafficlight_main_main_timer_running_red, trafficlight_main_main_tim
 transition (trafficlight_main_main_timer_running_green, trafficlight_main_main_timer_running_green) {
     name = "after 1s / counter -= 1"
     after = $
-            Float function after(attributes : Element, parameters : Element):
+            Float function after(attributes : Element):
                 return 1.0!
         $
     script = $