SCCD_execute.alc 27 KB

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