SCCD_execute.alc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. include "primitives.alh"
  2. include "modelling.alh"
  3. include "object_operations.alh"
  4. include "utils.alh"
  5. include "random.alh"
  6. include "library.alh"
  7. Void function print_states(model : Element, data : Element):
  8. Element classes
  9. Element states
  10. Element class
  11. String state
  12. log("Current states:")
  13. classes = set_copy(data["classes"])
  14. while (read_nr_out(classes) > 0):
  15. class = set_pop(classes)
  16. log(string_join(string_join(string_join(" ", class["ID"]), " : "), read_attribute(model, class["type"], "name")))
  17. log(" Attributes: " + dict_to_string(class["attributes"]))
  18. states = set_copy(class["states"])
  19. log(" States:")
  20. while (read_nr_out(states) > 0):
  21. state = set_pop(states)
  22. log(string_join(" ", read_attribute(model, state, "name")))
  23. return!
  24. Element function filter(model : Element, set : Element, attribute_name : String, attribute_value : Element):
  25. Element keys
  26. String key
  27. Element result
  28. result = create_node()
  29. while (read_nr_out(set) > 0):
  30. key = set_pop(set)
  31. if (value_eq(read_attribute(model, key, attribute_name), attribute_value)):
  32. set_add(result, key)
  33. return result!
  34. Element function filter_exists(model : Element, set : Element, attribute_name : String):
  35. Element keys
  36. String key
  37. Element result
  38. result = create_node()
  39. while (read_nr_out(set) > 0):
  40. key = set_pop(set)
  41. if (element_neq(read_attribute(model, key, attribute_name), read_root())):
  42. set_add(result, key)
  43. return result!
  44. Element function expand_state(model : Element, state : String):
  45. String t
  46. t = read_type(model, state)
  47. if (t == "SCCD/CompositeState"):
  48. // Recurse further in the composite
  49. return expand_composite_state(model, state)!
  50. elif (t == "SCCD/ParallelState"):
  51. // Split up all components
  52. return expand_parallel_state(model, state)!
  53. else:
  54. // Probably just an atomic, so return this one only
  55. Element result
  56. result = create_node()
  57. set_add(result, state)
  58. return result!
  59. Element function expand_composite_state(model : Element, composite_state : String):
  60. // Resolve all initial states from a single composite state
  61. String initial
  62. // Fetch the initial state
  63. initial = set_pop(filter(model, allAssociationDestinations(model, composite_state, "SCCD/composite_children"), "isInitial", True))
  64. // Expand the initial state, depending on what it is
  65. return expand_state(model, initial)!
  66. Element function expand_parallel_state(model : Element, parallel_state : String):
  67. // Resolve all initial states from a single parallel state
  68. Element children
  69. Element result
  70. Element expanded_children
  71. children = allAssociationDestinations(model, parallel_state, "SCCD/parallel_children")
  72. result = create_node()
  73. while (read_nr_out(children) > 0):
  74. set_merge(result, expand_state(model, set_pop(children)))
  75. return result!
  76. Void function start_class(model : Element, data : Element, class : String):
  77. // Start up the class and assign its initial state to it
  78. // Create the data structure for a running class
  79. Element class_handle
  80. class_handle = create_node()
  81. dict_add(class_handle, "type", class)
  82. dict_add(class_handle, "ID", cast_id2s(create_node()))
  83. dict_add(class_handle, "timers", create_node())
  84. // Add the current state of the class
  85. String initial_state
  86. // Should only be one behaviour linked to it!
  87. initial_state = set_pop(allAssociationDestinations(model, class, "SCCD/behaviour"))
  88. dict_add(class_handle, "states", expand_state(model, initial_state))
  89. // Add all attributes
  90. Element attributes
  91. attributes = create_node()
  92. Element attrs
  93. attrs = allAssociationDestinations(model, class, "SCCD/class_attributes")
  94. while (read_nr_out(attrs) > 0):
  95. dict_add(attributes, read_attribute(model, set_pop(attrs), "name"), read_root())
  96. dict_add(class_handle, "attributes", attributes)
  97. dict_add(data["classes"], class_handle["ID"], class_handle)
  98. return!
  99. Element function get_enabled_transitions(model : Element, state : String, data : Element, class : String):
  100. // Returns all enabled transitions
  101. // TODO ignore conditions and afters
  102. Element result
  103. Element to_filter
  104. String attr
  105. String transition
  106. Element cond
  107. result = create_node()
  108. to_filter = allOutgoingAssociationInstances(model, state, "SCCD/transition")
  109. while (read_nr_out(to_filter) > 0):
  110. transition = set_pop(to_filter)
  111. // Check event
  112. attr = read_attribute(model, transition, "event")
  113. if (bool_not(bool_or(element_eq(attr, read_root()), set_in(data["events"], attr)))):
  114. continue!
  115. // Check after
  116. // Only an after if there was no event!
  117. if (bool_and(element_eq(attr, read_root()), read_attribute(model, transition, "after"))):
  118. if (dict_in(data["classes"][class]["timers"], transition)):
  119. // Registered timer already, let's check if it has expired
  120. if (float_gt(data["classes"][class]["timers"][transition], time())):
  121. // Not enabled yet
  122. continue!
  123. else:
  124. // Not registered even, so not enabled either
  125. continue!
  126. // Check condition
  127. cond = read_attribute(model, transition, "cond")
  128. if (element_neq(cond, read_root())):
  129. cond = get_func_AL_model(import_node(cond))
  130. // Execute condition
  131. if (bool_not(cond(data["classes"][class]["attributes"]))):
  132. // Condition false, so skip
  133. continue!
  134. // All is OK for this transition
  135. set_add(result, transition)
  136. return result!
  137. Element function execute_transition(model : Element, data : Element, class : String, transition : String):
  138. // Execute the script (if any)
  139. Element script
  140. script = read_attribute(model, transition, "script")
  141. if (element_neq(script, read_root())):
  142. script = get_func_AL_model(import_node(script))
  143. script(data["classes"][class]["attributes"])
  144. // Raise events (if any)
  145. Element events
  146. String event
  147. events = allAssociationDestinations(model, transition, "SCCD/transition_raises")
  148. while (read_nr_out(events) > 0):
  149. event = set_pop(events)
  150. set_add(data["events"], read_attribute(model, event, "event"))
  151. // Return new set of states
  152. return expand_state(model, readAssociationDestination(model, transition))!
  153. Boolean function step_class(model : Element, data : Element, class : String):
  154. // Find enabled transitions in a class and execute it, updating the state
  155. // Iterate over all current states, searching for enabled transitions
  156. // Search for enabled transitions in higher levels as well!
  157. Element states
  158. Element new_states
  159. String state
  160. Element transitions
  161. String transition
  162. Boolean transitioned
  163. states = set_copy(data["classes"][class]["states"])
  164. new_states = create_node()
  165. transitioned = False
  166. while (read_nr_out(states) > 0):
  167. state = set_pop(states)
  168. // Fetch transitions in this state specifically (NO parent)
  169. transitions = get_enabled_transitions(model, state, data, class)
  170. if (read_nr_out(transitions) != 0):
  171. // Found an enabled transition, so store that one
  172. transition = random_choice(transitions)
  173. // Execute transition
  174. set_merge(new_states, execute_transition(model, data, class, transition))
  175. transitioned = True
  176. else:
  177. // Try going to the parent
  178. // TODO
  179. // Nothing found, so stay in the current state
  180. set_add(new_states, state)
  181. // Update states
  182. dict_overwrite(data["classes"][class], "states", new_states)
  183. reschedule_timeouts(model, data, class)
  184. return transitioned!
  185. Void function reschedule_timeouts(model : Element, data : Element, class : String):
  186. Element timed_transitions
  187. Element old_timed_transitions
  188. String transition
  189. Element states
  190. String state
  191. timed_transitions = create_node()
  192. states = set_copy(data["classes"][class]["states"])
  193. // Collect all timed transitions that are currently active
  194. while (read_nr_out(states) > 0):
  195. state = set_pop(states)
  196. // NOTE this set_merge does not eliminate duplicates, though this should happen later on when adding the timer (see other NOTE)
  197. timed_transitions = set_merge(timed_transitions, filter_exists(model, allOutgoingAssociationInstances(model, state, "SCCD/transition"), "after"))
  198. // Remove timers that no longer exist
  199. old_timed_transitions = dict_keys(data["classes"][class]["timers"])
  200. while (read_nr_out(old_timed_transitions) > 0):
  201. transition = set_pop(old_timed_transitions)
  202. if (bool_not(set_in(timed_transitions, transition))):
  203. // Transition is no longer scheduled for any state, so remove
  204. dict_delete(data["classes"][class]["timers"], transition)
  205. // Schedule timers that are not already scheduled
  206. while (read_nr_out(timed_transitions) > 0):
  207. transition = set_pop(timed_transitions)
  208. // NOTE Normally, a timer will not be added twice here, as the previous occurence will already find it
  209. if (bool_not(dict_in(data["classes"][class]["timers"], transition))):
  210. // Not yet scheduled this transition: do so now
  211. Element after
  212. Float after_duration
  213. after = read_attribute(model, transition, "after")
  214. after = get_func_AL_model(import_node(after))
  215. after_duration = after(data["classes"][class]["attributes"])
  216. dict_add(data["classes"][class]["timers"], transition, float_addition(data["start_time"], after_duration))
  217. return !
  218. Float function step(model : Element, data : Element):
  219. // Step through all classes
  220. Element classes
  221. Element class
  222. Float t_min
  223. Float t_current
  224. Boolean transitioned
  225. Element keys
  226. String key
  227. t_min = time() + 99999.0
  228. classes = dict_keys(data["classes"])
  229. // TODO this should use simulated time or something
  230. dict_overwrite(data, "start_time", time())
  231. transitioned = False
  232. while (read_nr_out(classes) > 0):
  233. class = set_pop(classes)
  234. if (step_class(model, data, class)):
  235. transitioned = True
  236. if (bool_not(transitioned)):
  237. // Find minimum timer for this class, and store that
  238. keys = dict_keys(data["classes"][class]["timers"])
  239. while (read_nr_out(keys) > 0):
  240. key = set_pop(keys)
  241. t_current = data["classes"][class]["timers"][key]
  242. if (t_current < t_min):
  243. t_min = t_current
  244. if (transitioned):
  245. // Do another step, as we can transition
  246. return 0.0!
  247. else:
  248. return float_subtraction(t_min, data["start_time"])!
  249. Boolean function main(model : Element):
  250. // Executes the provided SCCD model
  251. Element data
  252. data = create_node()
  253. dict_add(data, "classes", create_node())
  254. // Prepare for input
  255. output("Ready for input!")
  256. // Find initial
  257. String default_class
  258. default_class = set_pop(filter(model, allInstances(model, "SCCD/Class"), "default", True))
  259. // Start up the default class
  260. start_class(model, data, default_class)
  261. Float timeout
  262. Element interrupt
  263. timeout = 0.0
  264. while (True):
  265. print_states(model, data)
  266. interrupt = input_timeout(timeout)
  267. if (value_eq(interrupt, "#EXIT#")):
  268. // Stop execution
  269. return True!
  270. dict_overwrite(data, "events", create_node())
  271. if (element_neq(interrupt, read_root())):
  272. // Got interrupt
  273. log("Got event: " + cast_v2s(interrupt))
  274. set_add(data["events"], interrupt)
  275. output("Processed event, ready for more!")
  276. timeout = step(model, data)
  277. log("Pausing for " + cast_v2s(timeout))
  278. // We should never get here!
  279. return False!