SCCD_execute.alc 23 KB

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