reachability.alc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. include "primitives.alh"
  2. include "modelling.alh"
  3. include "object_operations.alh"
  4. include "transform.alh"
  5. include "constructors.alh"
  6. Boolean function reachability_graph(model : Element):
  7. Element workset
  8. Element transition_vectors_produce
  9. Element transition_vectors_consume
  10. Element all_transitions
  11. Element all_transitions_original
  12. Element enabled_transitions_set
  13. Element tv
  14. Element links
  15. Element mappings
  16. Element reachable_states
  17. Element keys
  18. Element enabled_transitions
  19. Element enabled_copy
  20. Element workset_copy
  21. Element curr_workset_el
  22. String transition
  23. String new_transition
  24. String tmp_transition
  25. String state
  26. String name
  27. String link
  28. String place
  29. String key
  30. String curr_transition
  31. String bp_name
  32. String curr_bp_name
  33. String to_construct
  34. String disable_on_trigger_str
  35. String user_input_s
  36. Integer link_weight
  37. Integer initial
  38. Integer state_id
  39. Integer next_id
  40. Integer user_input_i
  41. Boolean possible
  42. Boolean disable_on_trigger
  43. Element all_places
  44. Element dict_repr
  45. Element new_dict_repr
  46. Element work_unit
  47. Element curr_bp
  48. Element bp_keys
  49. Boolean bp_triggered
  50. Element reachable_keys_copy
  51. Element curr_reachable
  52. Element new_breakpoint
  53. Element curr_bp_function
  54. Element breakpoints
  55. breakpoints = create_node()
  56. Element cache
  57. cache = create_node()
  58. String user_input
  59. user_input = ""
  60. String mode
  61. mode = "paused"
  62. while (True):
  63. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual", "add_breakpoint"]}')
  64. output('{"msg_type": "mode_change", "mode": "paused"}')
  65. user_input = input()
  66. if (user_input == "step"):
  67. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual"]}')
  68. break!
  69. if (user_input == "continuous"):
  70. output('{"msg_type": "enabled_commands", "commands": []}')
  71. output('{"msg_type": "mode_change", "mode": "running"}')
  72. break!
  73. if (user_input == "manual"):
  74. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual"]}')
  75. break!
  76. if (user_input == "add_breakpoint"):
  77. bp_name = ""
  78. while (bool_or(bp_name == "", dict_in(breakpoints, bp_name))):
  79. output('{"msg_type": "input_required", "name": "breakpoint name", "type": "string"}')
  80. bp_name = input()
  81. output('{"msg_type": "input_required", "name": "disable on trigger?", "type": "boolean"}')
  82. disable_on_trigger_str = input()
  83. if (disable_on_trigger_str == "True"):
  84. disable_on_trigger = True
  85. else:
  86. disable_on_trigger = False
  87. output('{"msg_type": "input_required", "name": "breakpoint function", "type": "code"}')
  88. new_breakpoint = create_node()
  89. dict_add_fast(new_breakpoint, "function", construct_function())
  90. dict_add_fast(new_breakpoint, "disable_on_trigger", disable_on_trigger)
  91. dict_add_fast(new_breakpoint, "enabled", True)
  92. dict_add_fast(breakpoints, bp_name, new_breakpoint)
  93. output(('{"msg_type": "state_change", "state": "breakpoint_added", "breakpoint_name": "' + bp_name) + '"}')
  94. mode = user_input
  95. // Create a dictionary representation for each transition
  96. transition_vectors_produce = create_node()
  97. transition_vectors_consume = create_node()
  98. all_transitions = allInstances(model, "PetriNet/Transition")
  99. while (read_nr_out(all_transitions) > 0):
  100. transition = set_pop(all_transitions)
  101. String transition_name
  102. transition_name = read_attribute(model, transition, "name")
  103. tv = create_node()
  104. links = allIncomingAssociationInstances(model, transition, "PetriNet/P2T")
  105. while (read_nr_out(links) > 0):
  106. link = set_pop(links)
  107. name = reverseKeyLookup(model["model"], read_edge_src(model["model"][link]))
  108. link_weight = read_attribute(model, link, "weight")
  109. dict_add_fast(tv, name, link_weight)
  110. dict_add_fast(transition_vectors_consume, transition_name, tv)
  111. tv = create_node()
  112. links = allOutgoingAssociationInstances(model, transition, "PetriNet/T2P")
  113. while (read_nr_out(links) > 0):
  114. link = set_pop(links)
  115. name = reverseKeyLookup(model["model"], read_edge_dst(model["model"][link]))
  116. link_weight = read_attribute(model, link, "weight")
  117. dict_add_fast(tv, name, link_weight)
  118. dict_add_fast(transition_vectors_produce, transition_name, tv)
  119. workset = create_node()
  120. all_places = allInstances(model, "PetriNet/Place")
  121. dict_repr = create_node()
  122. while (read_nr_out(all_places) > 0):
  123. place = set_pop(all_places)
  124. dict_add_fast(dict_repr, place, read_attribute(model, place, "tokens"))
  125. all_transitions_original = allInstances(model, "PetriNet/Transition")
  126. mappings = create_node()
  127. state_id = 0
  128. next_id = 1
  129. reachable_states = create_node()
  130. dict_add_fast(reachable_states, state_id, dict_repr)
  131. dict_add_fast(mappings, state_id, create_node())
  132. // And add in the model itself
  133. state = instantiate_node(model, "ReachabilityGraph/InitialState", cast_i2s(state_id))
  134. instantiate_attribute(model, state, "name", cast_i2s(state_id))
  135. instantiate_attribute(model, state, "error", False)
  136. keys = dict_keys(dict_repr)
  137. while (read_nr_out(keys) > 0):
  138. key = set_pop(keys)
  139. place = instantiate_node(model, "ReachabilityGraph/Place", "")
  140. instantiate_attribute(model, place, "name", read_attribute(model, key, "name"))
  141. instantiate_attribute(model, place, "tokens", dict_repr[key])
  142. instantiate_link(model, "ReachabilityGraph/Contains", "", state, place)
  143. enabled_transitions = create_node()
  144. dict_repr = reachable_states[state_id]
  145. enabled_transitions_set = create_node()
  146. dict_add(enabled_transitions, state_id, enabled_transitions_set)
  147. // Compute how the PN behaves with this specific state
  148. // For this, we fetch all transitions and check if they are enabled
  149. all_transitions = set_copy(all_transitions_original)
  150. while (read_nr_out(all_transitions) > 0):
  151. transition = set_pop(all_transitions)
  152. String transition_name
  153. transition_name = read_attribute(model, transition, "name")
  154. keys = dict_keys(transition_vectors_consume[transition_name])
  155. possible = True
  156. while (read_nr_out(keys) > 0):
  157. key = set_pop(keys)
  158. // Compare the values in the state with those consumed by the transition
  159. if (integer_lt(dict_repr[key], transition_vectors_consume[transition_name][key])):
  160. // Impossible transition, so discard this one
  161. possible = False
  162. break!
  163. if (possible):
  164. set_add(enabled_transitions_set, transition_name)
  165. if (read_nr_out(enabled_transitions[state_id]) > 0):
  166. set_add(workset, state_id)
  167. if (bool_or(mode == "step", mode == "manual")):
  168. output('{"msg_type": "state_change", "state": "initialized"}')
  169. Element dict_values
  170. Element all_values
  171. String place
  172. dict_values = create_node()
  173. all_values = allAssociationDestinations(model, cast_i2s(state_id), "ReachabilityGraph/Contains")
  174. while (read_nr_out(all_values) > 0):
  175. place = set_pop(all_values)
  176. dict_add(dict_values, read_attribute(model, place, "name"), cast_v2s(read_attribute(model, place, "tokens")))
  177. String marking
  178. marking = dict_to_string(dict_values)
  179. output(((('{"msg_type": "node_added", "node_info": {"node_id": ' + cast_v2s(read_attribute(model, cast_i2s(state_id), "name"))) + ', "node_marking": ') + marking) + '}}')
  180. if (mode == "step"):
  181. mode = "paused"
  182. output('{"msg_type": "mode_change", "mode": "paused"}')
  183. while (True):
  184. if (mode == "manual"):
  185. break!
  186. output("Please enter your command. Options: continuous, step, manual")
  187. user_input = input()
  188. if (user_input == "step"):
  189. mode = user_input
  190. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual"]}')
  191. break!
  192. if (user_input == "continuous"):
  193. mode = user_input
  194. output('{"msg_type": "enabled_commands", "commands": []}')
  195. break!
  196. if (user_input == "manual"):
  197. mode = user_input
  198. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual"]}')
  199. break!
  200. while (read_nr_out(workset) > 0):
  201. if (mode == "manual"):
  202. String markings
  203. markings = '['
  204. workset_copy = set_copy(workset)
  205. while (read_nr_out(workset_copy) > 0):
  206. curr_workset_el = set_pop(workset_copy)
  207. markings = ((markings + '"') + cast_v2s(curr_workset_el)) + '"'
  208. if (read_nr_out(workset_copy) > 0):
  209. markings = markings + ", "
  210. markings = markings + "]"
  211. output(('{"msg_type": "unexplored_markings", "markings": ' + markings) + '}')
  212. log("Please choose a marking.")
  213. while (True):
  214. user_input_s = input()
  215. if (user_input_s == 'simulation_mode'):
  216. mode = input()
  217. if (mode != 'manual'):
  218. state_id = set_pop(workset)
  219. dict_repr = reachable_states[state_id]
  220. enabled_transitions_set = enabled_transitions[state_id]
  221. transition = set_pop(enabled_transitions_set)
  222. if (mode == 'continuous'):
  223. output('{"msg_type": "enabled_commands", "commands": []}')
  224. if (mode == "step"):
  225. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual"]}')
  226. break!
  227. elif (user_input_s == 'selected_marking'):
  228. user_input_i = cast_s2i(input())
  229. if (set_in(workset, user_input_i)):
  230. state_id = user_input_i
  231. enabled_transitions_set = enabled_transitions[state_id]
  232. String enabled_transitions_output
  233. enabled_transitions_output = '['
  234. enabled_copy = set_copy(enabled_transitions_set)
  235. while (read_nr_out(enabled_copy) > 0):
  236. curr_transition = set_pop(enabled_copy)
  237. enabled_transitions_output = enabled_transitions_output + cast_v2s(curr_transition)
  238. if (read_nr_out(enabled_copy) > 0):
  239. enabled_transitions_output = enabled_transitions_output + ", "
  240. enabled_transitions_output = enabled_transitions_output + "]"
  241. output(('{"msg_type": "enabled_transitions", "transitions": ' + enabled_transitions_output) + '}')
  242. output("Please choose an enabled transition or another marking.")
  243. elif (user_input_s == 'selected_transition'):
  244. set_remove(workset, state_id)
  245. dict_repr = reachable_states[state_id]
  246. user_input_s = input()
  247. transition = user_input_s
  248. set_remove(enabled_transitions_set, transition)
  249. break!
  250. else:
  251. state_id = set_pop(workset)
  252. dict_repr = reachable_states[state_id]
  253. enabled_transitions_set = enabled_transitions[state_id]
  254. transition = set_pop(enabled_transitions_set)
  255. if (read_nr_out(enabled_transitions[state_id]) > 0):
  256. set_add(workset, state_id)
  257. new_dict_repr = dict_copy(dict_repr)
  258. // Transition can execute, so compute and add the new state based on the consume/produce vectors
  259. keys = dict_keys(transition_vectors_consume[transition])
  260. while (read_nr_out(keys) > 0):
  261. key = set_pop(keys)
  262. dict_overwrite(new_dict_repr, key, integer_subtraction(new_dict_repr[key], transition_vectors_consume[transition][key]))
  263. keys = dict_keys(transition_vectors_produce[transition])
  264. while (read_nr_out(keys) > 0):
  265. key = set_pop(keys)
  266. dict_overwrite(new_dict_repr, key, integer_addition(new_dict_repr[key], transition_vectors_produce[transition][key]))
  267. // Check if this state already has an associated ID
  268. Integer other_state_id
  269. Integer target_id
  270. keys = dict_keys(reachable_states)
  271. target_id = -1
  272. Float start
  273. while (read_nr_out(keys) > 0):
  274. other_state_id = set_pop(keys)
  275. if (dict_eq(reachable_states[other_state_id], new_dict_repr)):
  276. target_id = other_state_id
  277. break!
  278. Boolean new_node
  279. new_node = False
  280. if (target_id == -1):
  281. new_node = True
  282. // New state
  283. target_id = next_id
  284. next_id = next_id + 1
  285. // Add to all data structures
  286. dict_add_fast(reachable_states, target_id, new_dict_repr)
  287. dict_add_fast(mappings, target_id, create_node())
  288. // And add in the model itself
  289. state = instantiate_node(model, "ReachabilityGraph/State", cast_i2s(target_id))
  290. instantiate_attribute(model, state, "name", cast_i2s(target_id))
  291. instantiate_attribute(model, state, "error", False)
  292. keys = dict_keys(new_dict_repr)
  293. Element sub
  294. String name
  295. while (read_nr_out(keys) > 0):
  296. key = set_pop(keys)
  297. name = read_attribute(model, key, "name")
  298. if (bool_not(dict_in(cache, name))):
  299. dict_add_fast(cache, name, create_node())
  300. sub = cache[name]
  301. if (bool_not(dict_in(sub, new_dict_repr[key]))):
  302. place = instantiate_node(model, "ReachabilityGraph/Place", "")
  303. instantiate_attribute(model, place, "name", name)
  304. instantiate_attribute(model, place, "tokens", new_dict_repr[key])
  305. dict_add_fast(sub, new_dict_repr[key], place)
  306. else:
  307. place = sub[new_dict_repr[key]]
  308. instantiate_link(model, "ReachabilityGraph/Contains", "", state, place)
  309. // Compute how the PN behaves with this specific state
  310. // For this, we fetch all transitions and check if they are enabled
  311. enabled_transitions_set = create_node()
  312. dict_add(enabled_transitions, target_id, enabled_transitions_set)
  313. all_transitions = set_copy(all_transitions_original)
  314. while (read_nr_out(all_transitions) > 0):
  315. tmp_transition = set_pop(all_transitions)
  316. String transition_name
  317. transition_name = read_attribute(model, tmp_transition, "name")
  318. keys = dict_keys(transition_vectors_consume[transition_name])
  319. possible = True
  320. while (read_nr_out(keys) > 0):
  321. key = set_pop(keys)
  322. // Compare the values in the state with those consumed by the transition
  323. if (integer_lt(new_dict_repr[key], transition_vectors_consume[transition_name][key])):
  324. // Impossible transition, so discard this one
  325. possible = False
  326. break!
  327. if (possible):
  328. set_add(enabled_transitions_set, transition_name)
  329. if (read_nr_out(enabled_transitions[target_id]) > 0):
  330. set_add(workset, target_id)
  331. // Anyway, we have found a transition, which we should store
  332. dict_add_fast(mappings[state_id], transition, target_id)
  333. // And also store it in the model itself
  334. new_transition = instantiate_link(model, "ReachabilityGraph/Transition", "", cast_i2s(state_id), cast_i2s(target_id))
  335. instantiate_attribute(model, new_transition, "name", transition)
  336. if (bool_or(mode == "step", mode == "manual")):
  337. if (new_node):
  338. Element dict_values
  339. Element all_values
  340. String place
  341. dict_values = create_node()
  342. all_values = allAssociationDestinations(model, cast_i2s(target_id), "ReachabilityGraph/Contains")
  343. while (read_nr_out(all_values) > 0):
  344. place = set_pop(all_values)
  345. dict_add(dict_values, read_attribute(model, place, "name"), read_attribute(model, place, "tokens"))
  346. String marking
  347. marking = dict_to_string(dict_values)
  348. output(((('{"msg_type": "node_added", "node_info": {"node_id": ' + cast_v2s(read_attribute(model, cast_i2s(target_id), "name"))) + ', "node_marking": ') + marking) + '}}')
  349. String link_output
  350. link_output = '{"msg_type": "link_added", "link_info": {'
  351. link_output = link_output + '"transition": '
  352. link_output = link_output + cast_v2s(transition)
  353. link_output = link_output + ', "transition_id": '
  354. link_output = link_output + cast_v2s(new_transition)
  355. link_output = link_output + ', "source": "'
  356. link_output = link_output + cast_v2s(state_id)
  357. link_output = link_output + '", "target": "'
  358. link_output = link_output + cast_v2s(target_id)
  359. link_output = link_output + '"}}'
  360. output(link_output)
  361. if (mode == "step"):
  362. mode = "paused"
  363. output('{"msg_type": "mode_change", "mode": "paused"}')
  364. output('{"msg_type": "state_change", "state": "step_done"}')
  365. bp_keys = dict_keys(breakpoints)
  366. bp_triggered = False
  367. while (read_nr_out(bp_keys) > 0):
  368. curr_bp_name = set_pop(bp_keys)
  369. curr_bp = breakpoints[curr_bp_name]
  370. curr_bp_function = curr_bp["function"]
  371. if (bool_and(curr_bp["enabled"], curr_bp_function(reachable_states, mappings))):
  372. output(('{"msg_type": "state_change", "state": "breakpoint_triggered", "breakpoint_name": "' + curr_bp_name) + '"}')
  373. bp_triggered = True
  374. log(curr_bp["disable_on_trigger"])
  375. if (curr_bp["disable_on_trigger"]):
  376. dict_delete(curr_bp, "enabled")
  377. dict_add_fast(curr_bp, "enabled", False)
  378. if (bp_triggered):
  379. Element all_states
  380. Element curr_state
  381. all_states = allInstances(model, "ReachabilityGraph/State")
  382. to_construct = '{"msg_type": "reachable_nodes", "nodes": ['
  383. while (read_nr_out(all_states) > 0):
  384. curr_state = set_pop(all_states)
  385. to_construct = to_construct + '{"node_id": '
  386. to_construct = to_construct + cast_v2s(curr_state)
  387. to_construct = to_construct + ', "node_marking": '
  388. Element dict_values
  389. Element all_values
  390. String place
  391. dict_values = create_node()
  392. all_values = allAssociationDestinations(model, curr_state, "ReachabilityGraph/Contains")
  393. while (set_len(all_values) > 0):
  394. place = set_pop(all_values)
  395. dict_add(dict_values, read_attribute(model, place, "name"), read_attribute(model, place, "tokens"))
  396. to_construct = to_construct + dict_to_string(dict_values)
  397. to_construct = to_construct + "}"
  398. if (read_nr_out(all_states) > 0):
  399. to_construct = to_construct + ', '
  400. to_construct = to_construct + ']}'
  401. output(to_construct)
  402. Element all_links
  403. Element curr_link
  404. all_links = allInstances(model, "ReachabilityGraph/Transition")
  405. to_construct = '{"msg_type": "node_links", "links": ['
  406. while (read_nr_out(all_links) > 0):
  407. curr_link = set_pop(all_links)
  408. to_construct = to_construct + '{"transition": '
  409. to_construct = to_construct + cast_v2s(read_attribute(model, curr_link, "name"))
  410. to_construct = to_construct + ', "transition_id": '
  411. to_construct = to_construct + cast_v2s(curr_link)
  412. to_construct = to_construct + ', "source": '
  413. to_construct = to_construct + cast_v2s(reverseKeyLookup(model["model"], read_edge_src(model["model"][curr_link])))
  414. to_construct = to_construct + ', "target": '
  415. to_construct = to_construct + cast_v2s(reverseKeyLookup(model["model"], read_edge_dst(model["model"][curr_link])))
  416. to_construct = to_construct + '}'
  417. if (read_nr_out(all_links) > 0):
  418. to_construct = to_construct + ', '
  419. to_construct = to_construct + ']}'
  420. output(to_construct)
  421. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual"]}')
  422. if (bool_not(mode == "paused")):
  423. mode = "paused"
  424. output('{"msg_type": "mode_change", "mode": "paused"}')
  425. if (bool_not(mode == "continuous")):
  426. while (True):
  427. if (mode == "manual"):
  428. break!
  429. output("Please enter your command. Options: continuous, step, manual")
  430. user_input = input()
  431. if (user_input == "step"):
  432. mode = user_input
  433. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual"]}')
  434. break!
  435. if (user_input == "continuous"):
  436. mode = user_input
  437. output('{"msg_type": "enabled_commands", "commands": []}')
  438. break!
  439. if (user_input == "manual"):
  440. mode = user_input
  441. output('{"msg_type": "enabled_commands", "commands": ["step", "continuous", "manual"]}')
  442. break!
  443. Element all_states
  444. Element curr_state
  445. all_states = allInstances(model, "ReachabilityGraph/State")
  446. to_construct = '{"msg_type": "reachable_nodes", "nodes": ['
  447. while (read_nr_out(all_states) > 0):
  448. curr_state = set_pop(all_states)
  449. to_construct = to_construct + '{"node_id": '
  450. to_construct = to_construct + cast_v2s(curr_state)
  451. to_construct = to_construct + ', "node_marking": '
  452. Element dict_values
  453. Element all_values
  454. String place
  455. dict_values = create_node()
  456. all_values = allAssociationDestinations(model, curr_state, "ReachabilityGraph/Contains")
  457. while (set_len(all_values) > 0):
  458. place = set_pop(all_values)
  459. dict_add(dict_values, read_attribute(model, place, "name"), read_attribute(model, place, "tokens"))
  460. to_construct = to_construct + dict_to_string(dict_values)
  461. to_construct = to_construct + "}"
  462. if (read_nr_out(all_states) > 0):
  463. to_construct = to_construct + ', '
  464. to_construct = to_construct + ']}'
  465. output(to_construct)
  466. Element all_links
  467. Element curr_link
  468. all_links = allInstances(model, "ReachabilityGraph/Transition")
  469. to_construct = '{"msg_type": "node_links", "links": ['
  470. while (read_nr_out(all_links) > 0):
  471. curr_link = set_pop(all_links)
  472. to_construct = to_construct + '{"transition": '
  473. to_construct = to_construct + cast_v2s(read_attribute(model, curr_link, "name"))
  474. to_construct = to_construct + ', "transition_id": '
  475. to_construct = to_construct + cast_v2s(curr_link)
  476. to_construct = to_construct + ', "source": '
  477. to_construct = to_construct + cast_v2s(reverseKeyLookup(model["model"], read_edge_src(model["model"][curr_link])))
  478. to_construct = to_construct + ', "target": '
  479. to_construct = to_construct + cast_v2s(reverseKeyLookup(model["model"], read_edge_dst(model["model"][curr_link])))
  480. to_construct = to_construct + '}'
  481. if (read_nr_out(all_links) > 0):
  482. to_construct = to_construct + ', '
  483. to_construct = to_construct + ']}'
  484. output(to_construct)
  485. output('{"msg_type": "mode_change", "mode": "finished"}')
  486. log("# reachable states: " + cast_v2s(next_id))
  487. return True!