SCCD_execute.alc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  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_current_state(model : Element, state : String, class_data : Element):
  45. // Find the hierarchy of all current states, and select those that contain the currently selected state
  46. Element result
  47. Element current_states
  48. result = create_node()
  49. current_states = set_copy(class_data["states"])
  50. Element hierarchy
  51. String deep_state
  52. while (read_nr_out(current_states) > 0):
  53. deep_state = set_pop(current_states)
  54. hierarchy = find_hierarchy(model, deep_state)
  55. // Got the hierarchy of one of the states
  56. if (set_in(hierarchy, state)):
  57. // This hierarchy contains the root state we are checking for, so add to set
  58. set_add(result, deep_state)
  59. return result!
  60. Element function expand_initial_state(model : Element, state : String, class_data : Element):
  61. String t
  62. t = read_type(model, state)
  63. if (t == "SCCD/CompositeState"):
  64. // Recurse further in the composite
  65. return expand_composite_state(model, state, class_data)!
  66. elif (t == "SCCD/ParallelState"):
  67. // Split up all components
  68. return expand_parallel_state(model, state, class_data)!
  69. elif (t == "SCCD/HistoryState"):
  70. // Reset the history
  71. // This is not really an initial state, but it is called in exactly the same places
  72. return class_data["history"][state]!
  73. else:
  74. // Probably just an atomic, so return this one only
  75. Element result
  76. result = create_node()
  77. set_add(result, state)
  78. return result!
  79. Element function expand_composite_state(model : Element, composite_state : String, class_data : Element):
  80. // Resolve all initial states from a single composite state
  81. String initial
  82. // Fetch the initial state
  83. initial = set_pop(filter(model, allAssociationDestinations(model, composite_state, "SCCD/composite_children"), "isInitial", True))
  84. // Expand the initial state, depending on what it is
  85. return expand_initial_state(model, initial, class_data)!
  86. Element function expand_parallel_state(model : Element, parallel_state : String, class_data : Element):
  87. // Resolve all initial states from a single parallel state
  88. Element children
  89. Element result
  90. Element expanded_children
  91. children = allAssociationDestinations(model, parallel_state, "SCCD/parallel_children")
  92. result = create_node()
  93. while (read_nr_out(children) > 0):
  94. set_merge(result, expand_initial_state(model, set_pop(children), class_data))
  95. return result!
  96. Void function delete_class(model : Element, data : Element, identifier : String):
  97. // Stop a specific class instance, with attached statechart, from executing
  98. dict_delete(data["classes"], identifier)
  99. Void function start_class(model : Element, data : Element, class : String, identifier : String, parameters : Element):
  100. // Start up the class and assign its initial state to it
  101. // Create the data structure for a running class
  102. Element class_handle
  103. class_handle = create_node()
  104. dict_add(class_handle, "type", class)
  105. dict_add(class_handle, "ID", identifier)
  106. dict_add(class_handle, "timers", create_node())
  107. // Add the current state of the class
  108. String initial_state
  109. // Should only be one behaviour linked to it!
  110. initial_state = set_pop(allAssociationDestinations(model, class, "SCCD/behaviour"))
  111. dict_add(class_handle, "states", expand_initial_state(model, initial_state, class_handle))
  112. // Initialize history for all composite states
  113. Element history
  114. Element cstates
  115. String cstate
  116. history = create_node()
  117. dict_add(class_handle, "history", history)
  118. cstates = allInstances(model, "SCCD/CompositeState")
  119. while (read_nr_out(cstates) > 0):
  120. cstate = set_pop(cstates)
  121. dict_add(history, cstate, expand_initial_state(model, cstate, class_handle))
  122. // Add all attributes
  123. Element attributes
  124. attributes = create_node()
  125. Element attrs
  126. attrs = allAssociationDestinations(model, class, "SCCD/class_attributes")
  127. while (read_nr_out(attrs) > 0):
  128. dict_add(attributes, read_attribute(model, set_pop(attrs), "name"), read_root())
  129. dict_add(class_handle, "attributes", attributes)
  130. // Invoke constructor
  131. Element constructor
  132. constructor = read_attribute(model, class, "constructor_body")
  133. if (element_neq(constructor, read_root())):
  134. // Constructor, so execute
  135. constructor = get_func_AL_model(import_node(constructor))
  136. constructor(attributes, parameters)
  137. // Execute all entry actions
  138. Element init
  139. init = create_node()
  140. set_add(init, "")
  141. // Initial state before initialization is the set with an empty hierarchy
  142. // Empty set would not find any difference between the source and target
  143. execute_actions(model, init, set_copy(class_handle["states"]), attributes, data, "")
  144. dict_add(data["classes"], class_handle["ID"], class_handle)
  145. return!
  146. Element function get_enabled_transitions(model : Element, state : String, data : Element, class : String):
  147. // Returns all enabled transitions
  148. Element result
  149. Element to_filter
  150. String attr
  151. String transition
  152. Element cond
  153. String evt_name
  154. Element evt
  155. Element events
  156. Element event_names
  157. Element event_parameters
  158. result = create_node()
  159. to_filter = allOutgoingAssociationInstances(model, state, "SCCD/transition")
  160. event_names = create_node()
  161. event_parameters = create_node()
  162. events = set_copy(data["events"])
  163. while (read_nr_out(events) > 0):
  164. evt = set_pop(events)
  165. evt_name = list_read(evt, 0)
  166. if (bool_not(set_in(event_names, evt_name))):
  167. // Not yet registered the event
  168. set_add(event_names, evt_name)
  169. dict_add(event_parameters, evt_name, create_node())
  170. // Add event parameters
  171. set_add(event_parameters[evt_name], list_read(evt, 1))
  172. while (read_nr_out(to_filter) > 0):
  173. transition = set_pop(to_filter)
  174. // Check event
  175. attr = read_attribute(model, transition, "event")
  176. if (bool_not(bool_or(element_eq(attr, read_root()), set_in(event_names, attr)))):
  177. // At least one enabled event is found
  178. continue!
  179. // Check after
  180. // Only an after if there was no event!
  181. if (bool_and(element_eq(attr, read_root()), read_attribute(model, transition, "after"))):
  182. if (dict_in(data["classes"][class]["timers"], transition)):
  183. // Registered timer already, let's check if it has expired
  184. if (float_gt(data["classes"][class]["timers"][transition], time())):
  185. // Not enabled yet
  186. continue!
  187. else:
  188. // Not registered even, so not enabled either
  189. continue!
  190. // Check condition, but depends on whether there was an event or not
  191. cond = read_attribute(model, transition, "cond")
  192. if (element_neq(cond, read_root())):
  193. // Got a condition, so resolve
  194. cond = get_func_AL_model(import_node(cond))
  195. if (element_neq(attr, read_root())):
  196. // We have an event to take into account!
  197. Element params
  198. Element param
  199. params = set_copy(event_parameters[attr])
  200. while (read_nr_out(params) > 0):
  201. param = set_pop(params)
  202. if (element_neq(cond, read_root())):
  203. // Got a condition to check first
  204. if (bool_not(cond(data["classes"][class]["attributes"], param))):
  205. // Condition failed, so skip
  206. continue!
  207. // Fine to add this one with the specified parameters
  208. set_add(result, create_tuple(transition, param))
  209. else:
  210. // No event to think about, just add the transition
  211. if (element_neq(cond, read_root())):
  212. // Check the condition first
  213. if (bool_not(cond(data["classes"][class]["attributes"], read_root()))):
  214. // Condition false, so skip
  215. continue!
  216. // Fine to add this one without event parameters (no event)
  217. set_add(result, create_tuple(transition, read_root()))
  218. return result!
  219. Void function process_raised_event(model : Element, event : Element, parameter_action : Element, data : Element):
  220. String scope
  221. scope = read_attribute(model, event, "scope")
  222. if (scope == "cd"):
  223. // Is an event for us internally, so don't append
  224. // Instead, we process it directly
  225. String operation
  226. operation = read_attribute(model, event, "event")
  227. if (operation == "create_instance"):
  228. // Start up a new class of the desired type
  229. // Parameters of this call:
  230. // class -- type of the class to instantiate
  231. // identifier -- name of this instance, for future reference
  232. // parameters -- parameters for constructor
  233. String class
  234. String identifier
  235. Element parameters
  236. class = set_pop(filter(model, allInstances(model, "SCCD/Class"), "name", list_read(parameter_action, 0)))
  237. identifier = list_read(parameter_action, 1)
  238. parameters = list_read(parameter_action, 2)
  239. start_class(model, data, class, identifier, parameters)
  240. elif (operation == "delete_instance"):
  241. // Delete the requested class
  242. String identifier
  243. identifier = list_read(parameter_action, 0)
  244. delete_class(model, data, identifier)
  245. else:
  246. set_add(data["events"], create_tuple(read_attribute(model, event, "event"), parameter_action))
  247. return !
  248. Element function execute_transition(model : Element, data : Element, class : String, transition_tuple : Element):
  249. // Execute the script (if any)
  250. Element script
  251. String transition
  252. Element event_parameter
  253. transition = list_read(transition_tuple, 0)
  254. event_parameter = list_read(transition_tuple, 1)
  255. script = read_attribute(model, transition, "script")
  256. if (element_neq(script, read_root())):
  257. script = get_func_AL_model(import_node(script))
  258. script(data["classes"][class]["attributes"], event_parameter)
  259. // Raise events (if any)
  260. Element events
  261. String event
  262. events = allAssociationDestinations(model, transition, "SCCD/transition_raises")
  263. while (read_nr_out(events) > 0):
  264. event = set_pop(events)
  265. Element parameter_action
  266. parameter_action = read_attribute(model, event, "parameter")
  267. if (element_neq(parameter_action, read_root())):
  268. // Got a parameter to evaluate
  269. parameter_action = get_func_AL_model(import_node(parameter_action))
  270. parameter_action = parameter_action(data["classes"][class]["attributes"], event_parameter)
  271. process_raised_event(model, event, parameter_action, data)
  272. // Find new set of states
  273. Element target_states
  274. Element source_states
  275. source_states = expand_current_state(model, readAssociationSource(model, transition), data["classes"][class])
  276. target_states = expand_initial_state(model, readAssociationDestination(model, transition), data["classes"][class])
  277. execute_actions(model, source_states, target_states, data["classes"][class], data, readAssociationSource(model, transition))
  278. return target_states!
  279. Boolean function step_class(model : Element, data : Element, class : String):
  280. // Find enabled transitions in a class and execute it, updating the state
  281. // Iterate over all current states, searching for enabled transitions
  282. // Search for enabled transitions in higher levels as well!
  283. Element states
  284. Element new_states
  285. String state
  286. Element transitions
  287. String transition
  288. Boolean transitioned
  289. Element hierarchy
  290. String current_state
  291. Boolean found
  292. states = set_copy(data["classes"][class]["states"])
  293. new_states = create_node()
  294. transitioned = False
  295. while (read_nr_out(states) > 0):
  296. state = set_pop(states)
  297. found = False
  298. // Loop over the hierarchy of this state and try to apply transitions
  299. hierarchy = find_hierarchy(model, state)
  300. while (read_nr_out(hierarchy) > 0):
  301. current_state = list_pop(hierarchy, 0)
  302. transitions = get_enabled_transitions(model, current_state, data, class)
  303. if (read_nr_out(transitions) > 0):
  304. // Found an enabled transition, so store that one
  305. transition = random_choice(transitions)
  306. log("Execute enabled transition: " + cast_v2s(read_attribute(model, list_read(transition, 0), "name")))
  307. // Execute transition
  308. set_merge(new_states, execute_transition(model, data, class, transition))
  309. // When leaving an orthogonal component, we must also pop all related states that might be processed in the future!
  310. Element leaving
  311. leaving = expand_current_state(model, current_state, data["classes"][class])
  312. set_difference(states, leaving)
  313. transitioned = True
  314. found = True
  315. break!
  316. if (bool_not(found)):
  317. // Nothing found, so stay in the current state
  318. set_add(new_states, state)
  319. // Update states
  320. dict_overwrite(data["classes"][class], "states", new_states)
  321. //reschedule_timeouts(model, data, class)
  322. return transitioned!
  323. Void function reschedule_timeouts(model : Element, data : Element, class : String):
  324. Element timed_transitions
  325. Element old_timed_transitions
  326. String transition
  327. Element states
  328. String state
  329. timed_transitions = create_node()
  330. states = set_copy(data["classes"][class]["states"])
  331. log("Rescheduling timeouts")
  332. // Collect all timed transitions that are currently active
  333. while (read_nr_out(states) > 0):
  334. state = set_pop(states)
  335. // NOTE this set_merge does not eliminate duplicates, though this should happen later on when adding the timer (see other NOTE)
  336. set_merge(timed_transitions, filter_exists(model, allOutgoingAssociationInstances(model, state, "SCCD/transition"), "after"))
  337. // Remove timers that no longer exist
  338. old_timed_transitions = dict_keys(data["classes"][class]["timers"])
  339. log("Got timed transitions: " + set_to_string(timed_transitions))
  340. log("Got currently scheduled transitions: " + set_to_string(old_timed_transitions))
  341. while (read_nr_out(old_timed_transitions) > 0):
  342. transition = set_pop(old_timed_transitions)
  343. log("Check remove " + cast_v2s(transition))
  344. if (bool_not(set_in(timed_transitions, transition))):
  345. // Transition is no longer scheduled for any state, so remove
  346. dict_delete(data["classes"][class]["timers"], transition)
  347. log("Remove timer for transition " + cast_v2s(read_attribute(model, transition, "name")))
  348. // Schedule timers that are not already scheduled
  349. while (read_nr_out(timed_transitions) > 0):
  350. transition = set_pop(timed_transitions)
  351. log("Check add " + cast_v2s(transition))
  352. // NOTE Normally, a timer will not be added twice here, as the previous occurence will already find it
  353. if (bool_not(dict_in(data["classes"][class]["timers"], transition))):
  354. // Not yet scheduled this transition: do so now
  355. Element after
  356. Float after_duration
  357. after = read_attribute(model, transition, "after")
  358. after = get_func_AL_model(import_node(after))
  359. after_duration = after(data["classes"][class]["attributes"])
  360. dict_add(data["classes"][class]["timers"], transition, float_addition(data["start_time"], after_duration))
  361. log("Add timer for transition " + cast_v2s(read_attribute(model, transition, "name")))
  362. log("All done!")
  363. return !
  364. String function get_parent(model : Element, state : String):
  365. Element tmp_set
  366. tmp_set = allAssociationOrigins(model, state, "SCCD/composite_children")
  367. set_merge(tmp_set, allAssociationOrigins(model, state, "SCCD/parallel_children"))
  368. if (read_nr_out(tmp_set) > 0):
  369. return set_pop(tmp_set)!
  370. else:
  371. return ""!
  372. Element function find_hierarchy(model : Element, state : String):
  373. if (state == ""):
  374. return create_node()!
  375. else:
  376. Element result
  377. String parent
  378. parent = get_parent(model, state)
  379. // We have a parent, so take the parent list first
  380. result = find_hierarchy(model, parent)
  381. list_append(result, state)
  382. return result!
  383. Void function execute_actions(model : Element, source_states : Element, target_states : Element, class_data : Element, data : Element, transition_source : String):
  384. Element exit
  385. Element entry
  386. exit = create_node()
  387. entry = create_node()
  388. source_states = set_copy(source_states)
  389. target_states = set_copy(target_states)
  390. // Add all exit and entry actions to the list of actions to execute
  391. // 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
  392. // First, find the hierarchy!
  393. Element hierarchy_sources
  394. Element hierarchy_targets
  395. Element all_hierarchies
  396. hierarchy_sources = create_node()
  397. while (read_nr_out(source_states) > 0):
  398. set_add(hierarchy_sources, find_hierarchy(model, set_pop(source_states)))
  399. hierarchy_targets = create_node()
  400. while (read_nr_out(target_states) > 0):
  401. set_add(hierarchy_targets, find_hierarchy(model, set_pop(target_states)))
  402. all_hierarchies = set_copy(hierarchy_sources)
  403. set_merge(all_hierarchies, hierarchy_targets)
  404. // Difference these all lists, finding the first common entry
  405. Element iter_hierarchies
  406. Integer i
  407. String current
  408. Element hierarchy
  409. Boolean finished
  410. Integer transition_depth
  411. Integer lca_depth
  412. lca_depth = 0
  413. finished = False
  414. if (transition_source != ""):
  415. // Find out the level of the transition_source by fetching its hierarchy
  416. transition_depth = list_len(find_hierarchy(model, transition_source)) - 1
  417. // Now check for the least common ancestor
  418. while (bool_not(finished)):
  419. // Check the i-th element in both and see if they are equal
  420. current = ""
  421. iter_hierarchies = set_copy(all_hierarchies)
  422. while (read_nr_out(iter_hierarchies) > 0):
  423. hierarchy = set_pop(iter_hierarchies)
  424. // Exhausted one of the lists
  425. if (lca_depth >= list_len(hierarchy)):
  426. finished = True
  427. break!
  428. // Reached the same level as transition depth already, so no need to increase
  429. if (lca_depth == transition_depth):
  430. finished = True
  431. break!
  432. // First entry, so read out value as reference
  433. if (current == ""):
  434. current = list_read(hierarchy, lca_depth)
  435. // Check with reference element
  436. if (bool_not(value_eq(list_read(hierarchy, lca_depth), current))):
  437. finished = True
  438. break!
  439. // i-th element equal for all hierarchies, so go to next element
  440. if (bool_not(finished)):
  441. lca_depth = lca_depth + 1
  442. if (lca_depth < transition_depth):
  443. i = lca_depth
  444. else:
  445. i = transition_depth
  446. else:
  447. // Initial, so just set i to zero
  448. i = 0
  449. // Found the first differing element at position i
  450. // All elements remaining in hierarchy_source are to be traversed in REVERSE order for the exit actions
  451. // All elements remaining in hierarchy_target are to be traversed in NORMAL order for the entry actions
  452. // This is not that simple either, as we need to consider that some actions might already have been added to the list...
  453. // Add hierarchy_sources actions
  454. String state
  455. Element visited
  456. Element action
  457. Element spliced_hierarchy
  458. Element hierarchy_source
  459. Element hierarchy_target
  460. visited = create_node()
  461. while (read_nr_out(hierarchy_sources) > 0):
  462. // Get one of these hierarchies
  463. hierarchy_source = set_pop(hierarchy_sources)
  464. spliced_hierarchy = list_splice(hierarchy_source, i, list_len(hierarchy_source))
  465. while (list_len(spliced_hierarchy) > 0):
  466. state = list_pop(spliced_hierarchy, list_len(spliced_hierarchy) - 1)
  467. if (set_in(visited, state)):
  468. // Already added this state, so don't bother
  469. continue!
  470. else:
  471. // New state, so prepend it to the list
  472. // Prepend, instead of append, as we want to do these operations in reverse order!
  473. list_insert(exit, state, 0)
  474. // Add this state as visited
  475. set_add(visited, state)
  476. // Add hierarchy_targets actions
  477. // Clear visited, just to be safe, though it should not matter
  478. visited = create_node()
  479. while (read_nr_out(hierarchy_targets) > 0):
  480. // Get one of these hierarchies
  481. hierarchy_target = set_pop(hierarchy_targets)
  482. spliced_hierarchy = list_splice(hierarchy_target, i, list_len(hierarchy_target))
  483. while (list_len(spliced_hierarchy) > 0):
  484. state = list_pop(spliced_hierarchy, list_len(spliced_hierarchy) - 1)
  485. if (set_in(visited, state)):
  486. // Already added this state, so don't bother
  487. continue!
  488. else:
  489. // New state, so append it to the list
  490. // Append, instead of prepend, as we want to do these operations in normal order!
  491. list_append(entry, state)
  492. // Add this state as visited, even though there might not have been an associated action
  493. set_add(visited, state)
  494. // Now we have a list of traversed states!
  495. // Start executing all their operations in order
  496. Element events
  497. String event
  498. // First do exit actions
  499. while (read_nr_out(exit) > 0):
  500. state = list_pop(exit, 0)
  501. log("EXIT " + cast_v2s(read_attribute(model, state, "name")))
  502. // Set history when leaving
  503. if (read_type(model, state) == "SCCD/CompositeState"):
  504. dict_overwrite(class_data["history"], state, expand_current_state(model, state, class_data))
  505. // Do exit actions
  506. action = read_attribute(model, state, "onExitScript")
  507. if (element_neq(action, read_root())):
  508. // Got a script, so execute!
  509. action = get_func_AL_model(import_node(action))
  510. action(class_data["attributes"])
  511. // Raise events
  512. events = allAssociationDestinations(model, state, "SCCD/onExitRaise")
  513. while (read_nr_out(events) > 0):
  514. event = set_pop(events)
  515. Element parameter_action
  516. parameter_action = read_attribute(model, event, "parameter")
  517. if (element_neq(parameter_action, read_root())):
  518. // Got a parameter to evaluate
  519. parameter_action = get_func_AL_model(import_node(parameter_action))
  520. parameter_action = parameter_action(class_data["attributes"])
  521. process_raised_event(model, event, parameter_action, data)
  522. // Unschedule after events
  523. Element timed_transitions
  524. timed_transitions = filter_exists(model, allOutgoingAssociationInstances(model, state, "SCCD/transition"), "after")
  525. log("Checking for unschedule of timed transitions: " + set_to_string(timed_transitions))
  526. while (read_nr_out(timed_transitions) > 0):
  527. log("Unschedule timer")
  528. dict_delete(class_data["timers"], set_pop(timed_transitions))
  529. // Then do entry actions
  530. while (read_nr_out(entry) > 0):
  531. state = list_pop(entry, 0)
  532. log("ENTRY " + cast_v2s(read_attribute(model, state, "name")))
  533. // Do entry actions
  534. action = read_attribute(model, state, "onEntryScript")
  535. if (element_neq(action, read_root())):
  536. // Got a script, so execute!
  537. action = get_func_AL_model(import_node(action))
  538. action(class_data["attributes"])
  539. // Raise events
  540. events = allAssociationDestinations(model, state, "SCCD/onEntryRaise")
  541. while (read_nr_out(events) > 0):
  542. event = set_pop(events)
  543. Element parameter_action
  544. parameter_action = read_attribute(model, event, "parameter")
  545. if (element_neq(parameter_action, read_root())):
  546. // Got a parameter to evaluate
  547. parameter_action = get_func_AL_model(import_node(parameter_action))
  548. parameter_action = parameter_action(class_data["attributes"])
  549. process_raised_event(model, event, parameter_action, data)
  550. // Schedule after events
  551. Element timed_transitions
  552. String transition
  553. Element after
  554. timed_transitions = filter_exists(model, allOutgoingAssociationInstances(model, state, "SCCD/transition"), "after")
  555. log("Checking for schedule of timed transitions: " + set_to_string(timed_transitions))
  556. while (read_nr_out(timed_transitions) > 0):
  557. log("Schedule timer")
  558. transition = set_pop(timed_transitions)
  559. after = get_func_AL_model(import_node(read_attribute(model, transition, "after")))
  560. dict_add(class_data["timers"], transition, float_addition(data["start_time"], after(class_data["attributes"])))
  561. return !
  562. Float function step(model : Element, data : Element):
  563. // Step through all classes
  564. Element classes
  565. Element class
  566. Float t_min
  567. Float t_current
  568. Boolean transitioned
  569. Element keys
  570. String key
  571. t_min = time() + 99999.0
  572. classes = dict_keys(data["classes"])
  573. // TODO this should use simulated time or something
  574. dict_overwrite(data, "start_time", time())
  575. transitioned = False
  576. while (read_nr_out(classes) > 0):
  577. class = set_pop(classes)
  578. if (step_class(model, data, class)):
  579. transitioned = True
  580. if (bool_not(transitioned)):
  581. // Find minimum timer for this class, and store that
  582. keys = dict_keys(data["classes"][class]["timers"])
  583. while (read_nr_out(keys) > 0):
  584. key = set_pop(keys)
  585. t_current = data["classes"][class]["timers"][key]
  586. if (t_current < t_min):
  587. t_min = t_current
  588. if (transitioned):
  589. // Do another step, as we can transition
  590. log("Transitioned, do another step!")
  591. return 0.0!
  592. else:
  593. log("Idle!")
  594. return float_subtraction(t_min, data["start_time"])!
  595. Boolean function main(model : Element):
  596. // Executes the provided SCCD model
  597. Element data
  598. data = create_node()
  599. dict_add(data, "classes", create_node())
  600. // Prepare for input
  601. output("Ready for input!")
  602. // Find initial
  603. String default_class
  604. default_class = set_pop(filter(model, allInstances(model, "SCCD/Class"), "default", True))
  605. // Start up the default class
  606. start_class(model, data, default_class, "main", read_root())
  607. Float timeout
  608. Element interrupt
  609. timeout = 0.0
  610. while (True):
  611. print_states(model, data)
  612. interrupt = input_timeout(timeout)
  613. if (value_eq(interrupt, "#EXIT#")):
  614. // Stop execution
  615. return True!
  616. dict_overwrite(data, "events", create_node())
  617. if (element_neq(interrupt, read_root())):
  618. // Got interrupt
  619. log("Got event: " + cast_v2s(interrupt))
  620. set_add(data["events"], create_tuple(interrupt, read_root()))
  621. output("Processed event, ready for more!")
  622. timeout = step(model, data)
  623. if (read_nr_out(data["classes"]) == 0):
  624. // No more active classes left: terminate!
  625. log("Finished SCCD execution")
  626. break!
  627. log("Pausing for " + cast_v2s(timeout))
  628. // We should never get here!
  629. return False!