|
@@ -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):
|