|
@@ -0,0 +1,214 @@
|
|
|
+include "primitives.alh"
|
|
|
+include "modelling.alh"
|
|
|
+include "object_operations.alh"
|
|
|
+include "utils.alh"
|
|
|
+include "random.alh"
|
|
|
+
|
|
|
+Void function print_states(model : Element, data : Element):
|
|
|
+ Element classes
|
|
|
+ Element states
|
|
|
+ Element class
|
|
|
+ String state
|
|
|
+
|
|
|
+ log("Current states:")
|
|
|
+ classes = set_copy(data["classes"])
|
|
|
+ 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")))
|
|
|
+
|
|
|
+ states = set_copy(class["states"])
|
|
|
+ while (read_nr_out(states) > 0):
|
|
|
+ state = set_pop(states)
|
|
|
+ log(string_join(" ", read_attribute(model, state, "name")))
|
|
|
+
|
|
|
+ return!
|
|
|
+
|
|
|
+Element function filter(model : Element, set : Element, attribute_name : String, attribute_value : Element):
|
|
|
+ Element keys
|
|
|
+ String key
|
|
|
+ Element result
|
|
|
+
|
|
|
+ result = create_node()
|
|
|
+ while (read_nr_out(set) > 0):
|
|
|
+ key = set_pop(set)
|
|
|
+ if (value_eq(read_attribute(model, key, attribute_name), attribute_value)):
|
|
|
+ set_add(result, key)
|
|
|
+
|
|
|
+ return result!
|
|
|
+
|
|
|
+Element function expand_state(model : Element, state : String):
|
|
|
+ String t
|
|
|
+ t = read_type(model, state)
|
|
|
+ if (t == "SCCD/CompositeState"):
|
|
|
+ // Recurse further in the composite
|
|
|
+ return expand_composite_state(model, state)!
|
|
|
+ elif (t == "SCCD/ParallelState"):
|
|
|
+ // Split up all components
|
|
|
+ return expand_parallel_state(model, state)!
|
|
|
+ else:
|
|
|
+ // Probably just an atomic, so return this one only
|
|
|
+ Element result
|
|
|
+ result = create_node()
|
|
|
+ set_add(result, state)
|
|
|
+ return result!
|
|
|
+
|
|
|
+Element function expand_composite_state(model : Element, composite_state : String):
|
|
|
+ // Resolve all initial states from a single composite state
|
|
|
+ String initial
|
|
|
+
|
|
|
+ // Fetch the initial state
|
|
|
+ initial = set_pop(filter(model, allAssociationDestinations(model, composite_state, "SCCD/composite_children"), "isInitial", True))
|
|
|
+ log("Got initial state: " + initial)
|
|
|
+
|
|
|
+ // Expand the initial state, depending on what it is
|
|
|
+ return expand_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 result
|
|
|
+ Element expanded_children
|
|
|
+
|
|
|
+ children = allAssociationDestinations(model, parallel_state, "SCCD/parallel_children")
|
|
|
+ result = create_node()
|
|
|
+
|
|
|
+ while (read_nr_out(children) > 0):
|
|
|
+ set_merge(result, expand_state(model, set_pop(children)))
|
|
|
+
|
|
|
+ return result!
|
|
|
+
|
|
|
+Void function start_class(model : Element, data : Element, class : String):
|
|
|
+ // Start up the class and assign its initial state to it
|
|
|
+
|
|
|
+ // Create the data structure for a running class
|
|
|
+ Element class_handle
|
|
|
+ class_handle = create_node()
|
|
|
+ dict_add(class_handle, "type", class)
|
|
|
+ dict_add(class_handle, "ID", cast_id2s(create_node()))
|
|
|
+
|
|
|
+ // Add the current state of the class
|
|
|
+ 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))
|
|
|
+
|
|
|
+ set_add(data["classes"], class_handle)
|
|
|
+
|
|
|
+ return!
|
|
|
+
|
|
|
+Element function get_enabled_transitions(model : Element, state : String, interrupt : Element):
|
|
|
+ // Returns all enabled transitions
|
|
|
+ // TODO ignore conditions and afters
|
|
|
+ Element result
|
|
|
+ Element to_filter
|
|
|
+ String attr
|
|
|
+ String transition
|
|
|
+
|
|
|
+ result = create_node()
|
|
|
+ to_filter = allOutgoingAssociationInstances(model, state, "SCCD/transition")
|
|
|
+
|
|
|
+ while (read_nr_out(to_filter) > 0):
|
|
|
+ transition = set_pop(to_filter)
|
|
|
+ attr = read_attribute(model, transition, "event")
|
|
|
+ if (element_eq(attr, read_root())):
|
|
|
+ // No event defined, so is fine
|
|
|
+ set_add(result, transition)
|
|
|
+ elif (value_eq(attr, interrupt)):
|
|
|
+ // Event defined, and that is the one of the event we got in
|
|
|
+ set_add(result, transition)
|
|
|
+
|
|
|
+ return result!
|
|
|
+
|
|
|
+Float function step_class(model : Element, data : Element, class : Element, interrupt : Element):
|
|
|
+ // Find enabled transitions in a class and execute it, updating the state
|
|
|
+ // Iterate over all current states, searching for enabled transitions
|
|
|
+ // Search for enabled transitions in higher levels as well!
|
|
|
+ Element states
|
|
|
+ Element new_states
|
|
|
+ String state
|
|
|
+ Element transitions
|
|
|
+ String transition
|
|
|
+
|
|
|
+ log(string_join("Stepping ", class["ID"]))
|
|
|
+ states = set_copy(class["states"])
|
|
|
+ new_states = create_node()
|
|
|
+
|
|
|
+ while (read_nr_out(states) > 0):
|
|
|
+ state = set_pop(states)
|
|
|
+
|
|
|
+ // Fetch transitions in this state specifically (NO parent)
|
|
|
+ transitions = get_enabled_transitions(model, state, interrupt)
|
|
|
+ log("Got enabled transitions: " + cast_v2s(read_nr_out(transitions)))
|
|
|
+ if (read_nr_out(transitions) != 0):
|
|
|
+ // Found an enabled transition, so store that one
|
|
|
+ transition = random_choice(transitions)
|
|
|
+ log("Executing enabled transition: " + cast_v2s(read_attribute(model, transition, "name")))
|
|
|
+
|
|
|
+ set_add(new_states, readAssociationDestination(model, transition))
|
|
|
+ log("Found new state: " + cast_v2s(read_attribute(model, readAssociationDestination(model, transition), "name")))
|
|
|
+ else:
|
|
|
+ // Try going to the parent
|
|
|
+ // TODO
|
|
|
+
|
|
|
+ // Nothing found, so stay in the current state
|
|
|
+ set_add(new_states, state)
|
|
|
+
|
|
|
+ // Update states
|
|
|
+ dict_overwrite(class, "states", new_states)
|
|
|
+
|
|
|
+ return 1.0!
|
|
|
+
|
|
|
+Float function step(model : Element, data : Element, interrupt : Element):
|
|
|
+ // Step through all classes
|
|
|
+ Element classes
|
|
|
+ Element class
|
|
|
+ Float t_min
|
|
|
+ Float t_class
|
|
|
+
|
|
|
+ t_min = 99999.0
|
|
|
+ classes = set_copy(data["classes"])
|
|
|
+
|
|
|
+ while (read_nr_out(classes) > 0):
|
|
|
+ class = set_pop(classes)
|
|
|
+ t_class = step_class(model, data, class, interrupt)
|
|
|
+ if (t_class < t_min):
|
|
|
+ t_min = t_class
|
|
|
+
|
|
|
+ return t_min!
|
|
|
+
|
|
|
+Boolean function main(model : Element):
|
|
|
+ // Executes the provided SCCD model
|
|
|
+ Element data
|
|
|
+ data = create_node()
|
|
|
+ dict_add(data, "classes", create_node())
|
|
|
+
|
|
|
+ // Prepare for input
|
|
|
+ output("Ready for input!")
|
|
|
+
|
|
|
+ // Find initial
|
|
|
+ String default_class
|
|
|
+ default_class = set_pop(filter(model, allInstances(model, "SCCD/Class"), "default", True))
|
|
|
+ log("Found default class: " + default_class)
|
|
|
+
|
|
|
+ // Start up the default class
|
|
|
+ start_class(model, data, default_class)
|
|
|
+
|
|
|
+ Float timeout
|
|
|
+ Element interrupt
|
|
|
+ timeout = 0.0
|
|
|
+ while (True):
|
|
|
+ print_states(model, data)
|
|
|
+ interrupt = input_timeout(timeout)
|
|
|
+
|
|
|
+ if (value_eq(interrupt, "#EXIT#")):
|
|
|
+ // Stop execution
|
|
|
+ return True!
|
|
|
+
|
|
|
+ if (element_neq(interrupt, read_root())):
|
|
|
+ // Got interrupt
|
|
|
+ log("Got event: " + cast_v2s(interrupt))
|
|
|
+ output("Processed event, ready for more!")
|
|
|
+
|
|
|
+ timeout = step(model, data, interrupt)
|
|
|
+
|
|
|
+ return True!
|