modelverse.py 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
  1. import urllib
  2. import json
  3. import random
  4. import sys
  5. import time
  6. import threading
  7. import uuid
  8. from sccd.runtime.statecharts_core import Event
  9. COMPILER_PATH = "interface/HUTN"
  10. MODE_UNCONNECTED = 0
  11. MODE_UNAUTHORIZED = 1
  12. MODE_MODELLING = 2
  13. MODE_MODIFY = 3
  14. MODE_DIALOG = 4
  15. MODE_MANUAL = 5
  16. MODE_SERVICE = 6
  17. # Bind to the compiler (might have to update path manually!)
  18. sys.path.append(COMPILER_PATH)
  19. from hutn_compiler.compiler import main as do_compile
  20. import socket2event
  21. # Exceptions
  22. class ModelverseException(Exception):
  23. pass
  24. class UnknownError(ModelverseException):
  25. pass
  26. class UnknownIdentifier(ModelverseException):
  27. pass
  28. class CompilationError(ModelverseException):
  29. pass
  30. class NoSuchAttribute(ModelverseException):
  31. pass
  32. class UnknownModel(ModelverseException):
  33. pass
  34. class ConnectionError(ModelverseException):
  35. pass
  36. class ModelExists(ModelverseException):
  37. pass
  38. class PermissionDenied(ModelverseException):
  39. pass
  40. class InvalidMode(ModelverseException):
  41. pass
  42. class InterfaceMismatch(ModelverseException):
  43. pass
  44. class UnknownMetamodellingHierarchy(ModelverseException):
  45. pass
  46. # Helper functions and configuration: do not use yourself!
  47. taskname = None
  48. mode = MODE_UNCONNECTED
  49. prev_mode = None
  50. current_model = None
  51. registered_metamodels = {}
  52. outputs = {}
  53. ctrl_input = None
  54. ctrl_output = None
  55. def _output_thread(controller, outp, task):
  56. req_out = controller.addOutputListener("request_out")
  57. my_id = str(uuid.uuid4())
  58. try:
  59. while 1:
  60. controller.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "get_output", "taskname": task}), my_id]))
  61. event = req_out.fetch(-1)
  62. if event.parameters[1] == my_id:
  63. outp[task].append(json.loads(event.parameters[0]))
  64. except:
  65. raise
  66. def _exec_on_statechart(statechart):
  67. def _exec_sc(controller, inport, outport):
  68. global taskname
  69. op = controller.addOutputListener(outport)
  70. while 1:
  71. if len(outputs[taskname]) > 1:
  72. # Is an output message of the Mv, so put it in the SC
  73. del outputs[taskname][0]
  74. output_event = outputs[taskname][0]
  75. if output_event == "Success" or output_event == "Failure":
  76. # Is a stop event!
  77. controller.addInput(Event("terminate", inport, []))
  78. break
  79. else:
  80. # Is just a normal event!
  81. controller.addInput(Event("input", inport, [output_event]))
  82. input_event = op.fetch(0)
  83. if input_event is not None:
  84. # Expand the event and make it HTTP input
  85. _input(input_event.parameters)
  86. time.sleep(0.02)
  87. thrd = threading.Thread(target=_exec_sc, args=statechart)
  88. thrd.daemon = True
  89. thrd.start()
  90. return None
  91. def _get_metamodel(model):
  92. global registered_metamodels
  93. try:
  94. return registered_metamodels[model]
  95. except KeyError:
  96. raise UnknownMetamodellingHierarchy(model)
  97. def _check_type(value):
  98. if not isinstance(value, (int, long, float, str, unicode, bool)):
  99. raise UnsupportedValue("%s : %s" % (value, str(type(value))))
  100. def _check_type_list(value):
  101. if isinstance(value, list):
  102. [_check_type(i) for i in value]
  103. else:
  104. _check_type(value)
  105. def _dict_to_list(python_dict):
  106. lst = []
  107. for k, v in python_dict.items():
  108. lst += [k, v]
  109. return lst
  110. def _goto_mode(new_mode, model_name=None):
  111. global mode
  112. if mode == MODE_MANUAL and new_mode == MODE_MODIFY:
  113. if model_name != None and current_model != model_name:
  114. raise InvalidMode("Mode error: cannot modify other models!")
  115. else:
  116. return
  117. elif mode == MODE_MODELLING and new_mode == MODE_MODIFY:
  118. # Are in root view, but want to modify a model
  119. _model_modify(model_name, _get_metamodel(model_name))
  120. elif mode == MODE_MODIFY and new_mode == MODE_MODIFY and model_name != None and current_model != model_name:
  121. # Are in modify mode, but want to modify a different model
  122. mm = _get_metamodel(model_name)
  123. _model_exit()
  124. _model_modify(model_name, mm)
  125. elif mode == MODE_MODIFY and new_mode == MODE_MODELLING:
  126. _model_exit()
  127. elif mode == new_mode:
  128. return
  129. else:
  130. # Go to a mode that we have no automatic transfer to: raise exception
  131. raise InvalidMode("Required mode: %s, current mode: %s" % (new_mode, mode))
  132. def _input(value, task=None):
  133. # Ugly json encoding of primitives
  134. #print("[IN] %s" % value)
  135. if task is None:
  136. task = taskname
  137. if isinstance(value, type([])):
  138. value = json.dumps(value)
  139. ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": task, "data": value}), None]))
  140. else:
  141. value = json.dumps(value)
  142. ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": task, "value": value}), None]))
  143. def _input_raw(value, taskname):
  144. # Ugly json encoding of primitives
  145. ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": taskname, "value": value}), None]))
  146. def _compile_AL(code):
  147. # Compile an action language file and send the compiled code
  148. code_fragments = code.split("\n")
  149. code_fragments = [i for i in code_fragments if i.strip() != ""]
  150. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  151. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  152. code_fragments = [i[initial_tabs:] for i in code_fragments]
  153. code_fragments.append("")
  154. code = "\n".join(code_fragments)
  155. with open(".code.alc", "w") as f:
  156. f.write(code)
  157. f.flush()
  158. compiled = do_compile(".code.alc", COMPILER_PATH + "/grammars/actionlanguage.g", "CS")
  159. return compiled
  160. def _compile_model(code):
  161. # Compile a model and send the compiled graph
  162. # First change multiple spaces to a tab
  163. code_fragments = code.split("\n")
  164. code_fragments = [i for i in code_fragments if i.strip() != ""]
  165. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  166. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  167. code_fragments = [i[initial_tabs:] for i in code_fragments]
  168. code_fragments.append("")
  169. code = "\n".join(code_fragments)
  170. with open(".model.mvc", "w") as f:
  171. f.write(code)
  172. f.flush()
  173. return do_compile(".model.mvc", COMPILER_PATH + "/grammars/modelling.g", "M")
  174. def _output(expected=None, task=None):
  175. if task is None:
  176. task = taskname
  177. try:
  178. while len(outputs[task]) < 2:
  179. time.sleep(0.02)
  180. del outputs[task][0]
  181. #print("[OUT] %s" % outputs[0])
  182. except:
  183. raise UnknownError()
  184. if expected is not None and _last_output(task) != expected:
  185. raise InterfaceMismatch(_last_output(task), expected)
  186. return _last_output(task)
  187. def _last_output(task=None):
  188. if task is None:
  189. task = taskname
  190. return outputs[task][0]
  191. # Raise common exceptions
  192. def _handle_output(requested=None, split=False):
  193. value = _output()
  194. if value.startswith("Model exists: "):
  195. raise ModelExists(value.split(": ", 1)[1])
  196. elif value.startswith("Permission denied"):
  197. raise PermissionDenied(value.split(": ", 1)[1])
  198. elif value.startswith("Model not found: "):
  199. raise UnknownModel(value.split(": ", 1)[1])
  200. elif value.startswith("Element not found: "):
  201. raise UnknownIdentifier(value.split(": ", 1)[1])
  202. elif value.startswith("Element exists: "):
  203. raise ElementExists(value.split(": ", 1)[1])
  204. elif value.startswith("Attribute not found: "):
  205. raise NoSuchAttribute(value.split(": ", 1)[1])
  206. elif requested is not None and value.startswith(requested):
  207. if not split:
  208. return value
  209. else:
  210. splitted = value.strip().split(" ", 1)
  211. if len(splitted) > 1:
  212. return splitted[1].split("\n")
  213. else:
  214. return []
  215. else:
  216. raise InterfaceMismatch(value)
  217. def _model_modify(model_name, metamodel_name):
  218. """Modify an existing model."""
  219. global mode
  220. global prev_mode
  221. if mode == MODE_MANUAL:
  222. prev_mode = MODE_MANUAL
  223. mode = MODE_MODIFY
  224. return None
  225. _goto_mode(MODE_MODELLING)
  226. prev_mode = MODE_MODELLING
  227. _input(["model_modify", model_name, metamodel_name])
  228. _handle_output("Success")
  229. global current_model
  230. current_model = model_name
  231. # Mode has changed
  232. mode = MODE_MODIFY
  233. _output("Model loaded, ready for commands!")
  234. def _model_exit():
  235. """Leave model modify mode."""
  236. global mode
  237. global prev_mode
  238. if prev_mode == MODE_MANUAL:
  239. mode = MODE_MANUAL
  240. return
  241. if mode != MODE_MODIFY:
  242. raise InvalidMode()
  243. _input("exit")
  244. _output("Success")
  245. mode = MODE_MODELLING
  246. def alter_context(model_name, metamodel_name):
  247. global registered_metamodels
  248. registered_metamodels[model_name] = metamodel_name
  249. def _start_http_client(addr, port, timeout):
  250. import http_client
  251. ctrl = http_client.Controller()
  252. listener = ctrl.addOutputListener("request_out")
  253. socket2event.boot_translation_service(ctrl)
  254. thrd = threading.Thread(target=ctrl.start)
  255. thrd.daemon = True
  256. thrd.start()
  257. evt = listener.fetch(-1)
  258. if evt.name != "http_client_initialized":
  259. raise Exception("HTTP client did not behave as expected during init: " + str(evt.name))
  260. ctrl.addInput(Event("connect", "request_in", [(addr, port), timeout]))
  261. evt = listener.fetch(-1)
  262. if evt.name == "http_client_timeout":
  263. raise Exception("HTTP client timeout")
  264. if evt.name != "http_client_ready":
  265. raise Exception("HTTP client did not behave as expected during connect: " + str(evt.name))
  266. return ctrl
  267. # Main MvC operations
  268. def init(address_param="127.0.0.1:8001", timeout=20.0):
  269. """Starts up the connection to the Modelverse."""
  270. # Start up the HTTP Client SC
  271. global ctrl_input
  272. global ctrl_output
  273. if ctrl_input is not None:
  274. ctrl_input.stop()
  275. if ctrl_output is not None:
  276. ctrl_output.stop()
  277. global address
  278. global port
  279. address, port = address_param.split(":", 1)
  280. port = int(port)
  281. ctrl_input = _start_http_client(address, port, timeout)
  282. ctrl_output = _start_http_client(address, port, timeout)
  283. controllers = [ctrl_input, ctrl_output]
  284. global mode
  285. start_time = time.time()
  286. task = str(random.random())
  287. _input_raw('"%s"' % task, "task_manager")
  288. mode = MODE_UNAUTHORIZED
  289. global outputs
  290. global taskname
  291. taskname = task
  292. outputs = {}
  293. _listen_to_output(ctrl_output, task)
  294. def _listen_to_output(controller, task):
  295. global outputs
  296. outputs[task] = [None]
  297. # This re-assign also diconnects the previous get_output connections to the outputs variable
  298. thrd = threading.Thread(target=_output_thread, args=[controller, outputs, task])
  299. thrd.daemon = True
  300. thrd.start()
  301. def login(username, password):
  302. """Log in a user, if user doesn't exist, it is created."""
  303. global mode
  304. _goto_mode(MODE_UNAUTHORIZED)
  305. _output("Log on as which user?")
  306. _input(username)
  307. if _output() == "Password for existing user?":
  308. _input(password)
  309. if _output() == "Welcome to the Model Management Interface v2.0!":
  310. _output("Use the 'help' command for a list of possible commands")
  311. _input("quiet")
  312. mode = MODE_MODELLING
  313. elif _last_output() == "Wrong password!":
  314. raise PermissionDenied()
  315. else:
  316. raise InterfaceMismatch(_last_output())
  317. elif _last_output() == "This is a new user: please give password!":
  318. _input(password)
  319. _output("Please repeat the password")
  320. _input(password)
  321. if _output() == "Passwords match!":
  322. _output("Welcome to the Model Management Interface v2.0!")
  323. _output("Use the 'help' command for a list of possible commands")
  324. _input("quiet")
  325. mode = MODE_MODELLING
  326. elif _last_output() == "Not the same password!":
  327. # We just sent the same password, so it should be identical, unless the interface changed
  328. raise InterfaceMismatch(_last_output())
  329. else:
  330. raise InterfaceMismatch(_last_output())
  331. else:
  332. raise InterfaceMismatch(_last_output())
  333. def model_add(model_name, metamodel_name, model_code=""):
  334. """Instantiate a new model."""
  335. _goto_mode(MODE_MODELLING)
  336. # Do this before creating the model, as otherwise compilation errors would make us inconsistent
  337. """
  338. if model_code is not None:
  339. try:
  340. compiled = _compile_model(model_code)
  341. except Exception as e:
  342. raise CompilationError(e)
  343. else:
  344. compiled = [0]
  345. """
  346. _input(["model_add", metamodel_name, model_name])
  347. _handle_output("Waiting for model constructors...")
  348. _input(model_code)
  349. _output("Success")
  350. global registered_metamodels
  351. registered_metamodels[model_name] = metamodel_name
  352. def upload_code(code):
  353. _input(code)
  354. """
  355. try:
  356. compiled = _compile_AL(code)
  357. except Exception as e:
  358. raise CompilationError(e)
  359. _input(compiled)
  360. """
  361. def model_delete(model_name):
  362. """Delete an existing model."""
  363. _goto_mode(MODE_MODELLING)
  364. _input(["model_delete", model_name])
  365. _handle_output("Success")
  366. def model_list(location):
  367. """List all models."""
  368. _goto_mode(MODE_MODELLING)
  369. _input(["model_list", location])
  370. return set(_handle_output("Success: ", split=True))
  371. def model_list_full(location):
  372. """List full information on all models."""
  373. _goto_mode(MODE_MODELLING)
  374. _input(["model_list_full", location])
  375. output = _handle_output("Success: ", split=True)
  376. lst = set([])
  377. for v in output:
  378. m = v.strip()
  379. perm, own, grp, m = m.split(" ", 3)
  380. lst.add((m, own, grp, perm))
  381. return lst
  382. def verify(model_name, metamodel_name=None):
  383. """Verify if a model conforms to its metamodel."""
  384. _goto_mode(MODE_MODELLING)
  385. if metamodel_name is None:
  386. metamodel_name = _get_metamodel(model_name)
  387. _input(["verify", model_name, metamodel_name])
  388. return _handle_output("Success: ", split=True)[0]
  389. def model_overwrite(model_name, new_model="", metamodel_name=None):
  390. """Upload a new model and overwrite an existing model."""
  391. _goto_mode(MODE_MODIFY, model_name)
  392. """
  393. if new_model is not None:
  394. try:
  395. compiled = _compile_model(new_model)
  396. except Exception as e:
  397. raise CompilationError(e)
  398. else:
  399. compiled = [0]
  400. """
  401. _input("upload")
  402. _handle_output("Waiting for model constructors...")
  403. _input(new_model)
  404. _output("Success")
  405. if metamodel_name is not None:
  406. global registered_metamodels
  407. registered_metamodels[model_name] = metamodel_name
  408. def user_logout():
  409. """Log out the current user and break the connection."""
  410. global mode
  411. _goto_mode(MODE_MODELLING)
  412. _input("exit")
  413. mode = MODE_UNCONNECTED
  414. def user_delete():
  415. """Removes the current user and break the connection."""
  416. global mode
  417. _goto_mode(MODE_MODELLING)
  418. _input("self-destruct")
  419. mode = MODE_UNCONNECTED
  420. def model_render(model_name, mapper_name):
  421. """Fetch a rendered verion of a model."""
  422. _goto_mode(MODE_MODELLING)
  423. _input(["model_render", model_name, mapper_name])
  424. return json.loads(_handle_output("Success: ", split=True)[0])
  425. def transformation_between(source, target):
  426. _goto_mode(MODE_MODELLING)
  427. _input(["transformation_between", source, target])
  428. output = _handle_output("Success: ", split=True)
  429. return set(output)
  430. def transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None):
  431. """Create a new model transformation."""
  432. global mode
  433. _goto_mode(MODE_MODELLING)
  434. """
  435. try:
  436. compiled = _compile_model(code)
  437. except Exception as e:
  438. raise CompilationError(e)
  439. """
  440. mv_dict_rep = _dict_to_list(source_metamodels) + [""] + _dict_to_list(target_metamodels) + [""]
  441. _input(["transformation_add_MT"] + mv_dict_rep + [operation_name])
  442. # Possibly modify the merged metamodel first (add tracability links)
  443. if len(source_metamodels) + len(target_metamodels) > 0:
  444. mode = MODE_MANUAL
  445. _output("Model loaded, ready for commands!")
  446. callback()
  447. _input("exit")
  448. mode = MODE_MODELLING
  449. # Done, so RAMify and upload the model
  450. _handle_output("Waiting for model constructors...")
  451. _input(code)
  452. _handle_output("Success")
  453. def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None):
  454. """Create a new action language model, which can be executed."""
  455. global mode
  456. _goto_mode(MODE_MODELLING)
  457. """
  458. try:
  459. compiled = _compile_AL(code)
  460. except Exception as e:
  461. raise CompilationError(e)
  462. """
  463. mv_dict_rep = _dict_to_list(source_metamodels) + [""] + _dict_to_list(target_metamodels) + [""]
  464. _input(["transformation_add_AL"] + mv_dict_rep + [operation_name])
  465. # Possibly modify the merged metamodel first (add tracability links)
  466. if len(source_metamodels) + len(target_metamodels) > 0:
  467. mode = MODE_MANUAL
  468. _output("Model loaded, ready for commands!")
  469. callback()
  470. _input("exit")
  471. mode = MODE_MODELLING
  472. _handle_output("Waiting for code constructors...")
  473. #_input(compiled)
  474. _input(code)
  475. _output("Success")
  476. def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=lambda: None):
  477. """Create a new manual model operation."""
  478. global mode
  479. _goto_mode(MODE_MODELLING)
  480. mv_dict_rep = _dict_to_list(source_metamodels) + [""] + _dict_to_list(target_metamodels) + [""]
  481. _input(["transformation_add_MANUAL"] + mv_dict_rep + [operation_name])
  482. # Possibly modify the merged metamodel first (add tracability links)
  483. if len(source_metamodels) + len(target_metamodels) > 0:
  484. mode = MODE_MANUAL
  485. _output("Model loaded, ready for commands!")
  486. callback()
  487. _input("exit")
  488. mode = MODE_MODELLING
  489. _handle_output("Success")
  490. def transformation_execute_AL(operation_name, input_models_dict, output_models_dict, statechart=None):
  491. """Execute an existing model operation."""
  492. global mode
  493. _goto_mode(MODE_MODELLING)
  494. mv_dict_rep = _dict_to_list(input_models_dict) + [""] + _dict_to_list(output_models_dict) + [""]
  495. _input(["transformation_execute", operation_name] + mv_dict_rep)
  496. _handle_output("Success: ready for AL execution")
  497. if statechart is not None:
  498. # We are to delegate this information to another statechart, which wants interaction
  499. # As such, we continue on a different thread, where we pipe the information, and let this call return immediately
  500. mode = MODE_DIALOG
  501. _exec_on_statechart(statechart)
  502. mode = MODE_MODELLING
  503. return None
  504. else:
  505. # No statechart associated, so just wait until we are finished
  506. while _output() not in ["Success", "Failure"]:
  507. pass
  508. if _last_output() == "Success":
  509. return True
  510. else:
  511. return False
  512. def transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  513. """Execute an existing model operation."""
  514. global mode
  515. _goto_mode(MODE_MODELLING)
  516. mv_dict_rep = _dict_to_list(input_models_dict) + [""] + _dict_to_list(output_models_dict) + [""]
  517. _input(["transformation_execute", operation_name] + mv_dict_rep)
  518. _handle_output("Success: ready for MANUAL execution")
  519. # Skip over the begin of mini_modify
  520. _handle_output("Please perform manual operation ")
  521. _output("Model loaded, ready for commands!")
  522. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  523. mode = MODE_MANUAL
  524. callback()
  525. # Finished, so leave
  526. _input("exit")
  527. mode = MODE_MODELLING
  528. # Got termination message, so we are done!
  529. if _output() == "Success":
  530. return True
  531. else:
  532. return False
  533. def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, statechart=None):
  534. """Execute an existing model operation."""
  535. global mode
  536. _goto_mode(MODE_MODELLING)
  537. mv_dict_rep = _dict_to_list(input_models_dict) + [""] + _dict_to_list(output_models_dict) + [""]
  538. _input(["transformation_execute", operation_name] + mv_dict_rep)
  539. _handle_output("Success: ready for MT execution")
  540. if statechart is not None:
  541. # We are to delegate this information to another statechart, which wants interaction
  542. # As such, we continue on a different thread, where we pipe the information, and let this call return immediately
  543. mode = MODE_DIALOG
  544. _exec_on_statechart(statechart)
  545. mode = MODE_MODELLING
  546. return None
  547. else:
  548. # No statechart associated, so just wait until we are finished
  549. while _output() not in ["Success", "Failure"]:
  550. pass
  551. if _last_output() == "Success":
  552. return True
  553. else:
  554. return False
  555. def transformation_list():
  556. """List existing model operations."""
  557. _goto_mode(MODE_MODELLING)
  558. _input("transformation_list")
  559. output = _handle_output("Success: ", split=True)
  560. lst = set([])
  561. for v in output:
  562. t, m = v.strip().split(" ", 1)
  563. t = t[1:-1].strip()
  564. m = m.strip().split(":")[0].strip()
  565. lst.add((t, m))
  566. return lst
  567. def process_execute(process_name, prefix, callbacks):
  568. """Execute a process model."""
  569. global mode
  570. _goto_mode(MODE_MODELLING)
  571. _input(["process_execute", process_name, prefix])
  572. _handle_output("Success")
  573. reuse = False
  574. while reuse or _output() != "Success":
  575. reuse = False
  576. output = _last_output()
  577. if output.startswith("Enacting "):
  578. # Next activity!
  579. t = output.split(" ", 1)[1].split(":", 1)[0]
  580. name = output.split(": ", 1)[1]
  581. if name in callbacks:
  582. callback = callbacks[name]
  583. if t == "ModelTransformation" or t == "ActionLanguage":
  584. while not (_output().startswith("Enacting ") or _last_output() == "Success"):
  585. mode = MODE_DIALOG
  586. reply = callback(_last_output())
  587. mode = MODE_MODELLING
  588. if reply is not None:
  589. _input(reply)
  590. if _last_output().startswith("Enacting "):
  591. reuse = True
  592. elif t == "ManualOperation":
  593. _handle_output("Please perform manual operation ")
  594. _output("Model loaded, ready for commands!")
  595. mode = MODE_MANUAL
  596. callback()
  597. _input("exit")
  598. mode = MODE_MODELLING
  599. def permission_modify(model_name, permissions):
  600. """Modify permissions of a model."""
  601. _goto_mode(MODE_MODELLING)
  602. _input(["permission_modify", model_name, permissions])
  603. _handle_output("Success")
  604. def permission_owner(model_name, owner):
  605. """Modify the owning user of a model."""
  606. _goto_mode(MODE_MODELLING)
  607. _input(["permission_owner", model_name, owner])
  608. _handle_output("Success")
  609. def permission_group(model_name, group):
  610. """Modify the owning group of a model."""
  611. _goto_mode(MODE_MODELLING)
  612. _input(["permission_group", model_name, group])
  613. _handle_output("Success")
  614. def group_create(group_name):
  615. """Create a new group."""
  616. _goto_mode(MODE_MODELLING)
  617. _input(["group_create", group_name])
  618. _handle_output("Success")
  619. def group_delete(group_name):
  620. """Delete a group of which you are an owner."""
  621. _goto_mode(MODE_MODELLING)
  622. _input(["group_delete", group_name])
  623. _handle_output("Success")
  624. def group_owner_add(group_name, user_name):
  625. """Add a new owning user to a group you own."""
  626. _goto_mode(MODE_MODELLING)
  627. _input(["owner_add", group_name, user_name])
  628. _handle_output("Success")
  629. def group_owner_delete(group_name, user_name):
  630. """Delete an owning user to a group you own."""
  631. _goto_mode(MODE_MODELLING)
  632. _input(["owner_delete", group_name, user_name])
  633. _handle_output("Success")
  634. def group_join(group_name, user_name):
  635. """Add a new user to a group you own."""
  636. _goto_mode(MODE_MODELLING)
  637. _input(["group_join", group_name, user_name])
  638. _handle_output("Success")
  639. def group_kick(group_name, user_name):
  640. """Delete a user from a group you own."""
  641. _goto_mode(MODE_MODELLING)
  642. _input(["group_kick", group_name, user_name])
  643. _handle_output("Success")
  644. def group_list():
  645. """List existing groups."""
  646. _goto_mode(MODE_MODELLING)
  647. _input(["group_list"])
  648. _handle_output("Success")
  649. def admin_promote(user_name):
  650. """Promote a user to admin status."""
  651. _goto_mode(MODE_MODELLING)
  652. _input(["admin_promote", user_name])
  653. _handle_output("Success")
  654. def admin_demote():
  655. """Demote a user from admin status."""
  656. _goto_mode(MODE_MODELLING)
  657. _input(["admin_demote", user_name])
  658. _handle_output("Success")
  659. # Actual operations on the model
  660. def element_list(model_name):
  661. """Return a list of all IDs and the type of the element"""
  662. _goto_mode(MODE_MODIFY, model_name)
  663. _input("list_full")
  664. lst = set([])
  665. output = _handle_output("Success: ", split=True)
  666. for v in output:
  667. m, mm = v.split(":")
  668. m = m.strip()
  669. mm = mm.strip()
  670. lst.add((m, mm))
  671. return lst
  672. def types(model_name):
  673. """Return a list of all types usable in the model"""
  674. _goto_mode(MODE_MODIFY, model_name)
  675. _input("types")
  676. lst = set([])
  677. output = _handle_output("Success: ", split=True)
  678. for v in output:
  679. m, mm = v.split(":")
  680. m = m.strip()
  681. lst.add(m)
  682. return lst
  683. def types_full(model_name):
  684. """Return a list of full types usable in the model"""
  685. _goto_mode(MODE_MODIFY, model_name)
  686. _input("types")
  687. lst = set([])
  688. output = _handle_output("Success: ", split=True)
  689. for v in output:
  690. m, mm = v.split(":")
  691. m = m.strip()
  692. mm = mm.strip()
  693. lst.add((m, mm))
  694. return lst
  695. def read(model_name, ID):
  696. """Return a tuple of information on the element: its type and source/target (None if not an edge)"""
  697. _goto_mode(MODE_MODIFY, model_name)
  698. _input(["read", ID])
  699. v = _handle_output("Success: ", split=True)
  700. t = v[1].split(":")[1].strip()
  701. if (not v[2].startswith("Source:")):
  702. rval = (t, None)
  703. else:
  704. src = v[2].split(":")[1].strip()
  705. trg = v[3].split(":")[1].strip()
  706. rval = (t, (src, trg))
  707. return rval
  708. def read_attrs(model_name, ID):
  709. """Return a dictionary of attribute value pairs"""
  710. _goto_mode(MODE_MODIFY, model_name)
  711. _input(["read", ID])
  712. output = _handle_output("Success: ", split=True)
  713. searching = True
  714. rval = {}
  715. for r in output:
  716. if searching:
  717. if r == "Attributes:":
  718. # Start working on attributes
  719. searching = False
  720. else:
  721. key, value = r.split(":", 1)
  722. _, value = value.split("=", 1)
  723. key = json.loads(key.strip())
  724. value = value.strip()
  725. if value == "None":
  726. value = None
  727. elif value == "True":
  728. value = True
  729. elif value == "False":
  730. value = False
  731. else:
  732. value = json.loads(value)
  733. rval[key] = value
  734. return rval
  735. def instantiate(model_name, typename, edge=None, ID=""):
  736. """Create a new instance of the specified typename, between the selected elements (if not None), and with the provided ID (if any)"""
  737. _goto_mode(MODE_MODIFY, model_name)
  738. if edge is None:
  739. _input(["instantiate_node", typename, ID])
  740. else:
  741. _input(["instantiate_edge", typename, ID, edge[0], edge[1]])
  742. return _handle_output("Success: ", split=True)[0]
  743. def delete_element(model_name, ID):
  744. """Delete the element with the given ID"""
  745. _goto_mode(MODE_MODIFY, model_name)
  746. _input(["delete", ID])
  747. _handle_output("Success")
  748. def attr_assign(model_name, ID, attr, value):
  749. """Assign a value to an attribute"""
  750. _check_type(value)
  751. _goto_mode(MODE_MODIFY, model_name)
  752. _input(["attr_add", ID, attr, value])
  753. _handle_output("Success")
  754. def attr_assign_code(model_name, ID, attr, code):
  755. """Assign a piece of Action Language code to the attribute"""
  756. _check_type(code)
  757. """
  758. try:
  759. compiled = _compile_AL(code)
  760. except Exception as e:
  761. raise CompilationError(e)
  762. """
  763. _goto_mode(MODE_MODIFY, model_name)
  764. _input(["attr_add_code", ID, attr])
  765. _handle_output("Waiting for code constructors...")
  766. #_input(compiled)
  767. _input(code)
  768. _output("Success")
  769. def attr_delete(model_name, ID, attr):
  770. """Remove an attribute."""
  771. _goto_mode(MODE_MODIFY, model_name)
  772. _input(["attr_del", ID, attr])
  773. _handle_output("Success")
  774. def read_outgoing(model_name, ID, typename):
  775. """Returns a list of all outgoing associations of a specific type ("" = all)"""
  776. _goto_mode(MODE_MODIFY, model_name)
  777. _input(["read_outgoing", ID, typename])
  778. output = _handle_output("Success: ", split=True)
  779. return set(output)
  780. def read_incoming(model_name, ID, typename):
  781. """Returns a list of all incoming associations of a specific type ("" = all)"""
  782. _goto_mode(MODE_MODIFY, model_name)
  783. _input(["read_incoming", ID, typename])
  784. output = _handle_output("Success: ", split=True)
  785. return set(output)
  786. def read_association_source(model_name, ID):
  787. """Returns the source of an association."""
  788. _goto_mode(MODE_MODIFY, model_name)
  789. _input(["read_association_source", ID])
  790. return _handle_output("Success: ", split=True)[0]
  791. def read_association_destination(model_name, ID):
  792. """Returns the destination of an association."""
  793. _goto_mode(MODE_MODIFY, model_name)
  794. _input(["read_association_destination", ID])
  795. return _handle_output("Success: ", split=True)[0]
  796. ##### To document:
  797. def service_register(name, function):
  798. """Register a function as a service with a specific name."""
  799. def service_process(service_task):
  800. global address
  801. global port
  802. ctrl = _start_http_client(address, port, 10.0)
  803. _listen_to_output(ctrl, service_task)
  804. while 1:
  805. client_task = service_get(service_task)
  806. ctrl = _start_http_client(address, port, 10.0)
  807. _listen_to_output(ctrl, client_task)
  808. thrd = threading.Thread(target=function, args=[client_task])
  809. thrd.daemon = True
  810. thrd.start()
  811. global mode
  812. _goto_mode(MODE_MODELLING)
  813. _input(["service_register", name])
  814. # Now we are in service-mode
  815. mode = MODE_SERVICE
  816. task = _handle_output("Success: ", split=True)[0]
  817. # Process events in the background!
  818. thrd = threading.Thread(target=service_process, args=[task])
  819. thrd.daemon = True
  820. thrd.start()
  821. def service_stop():
  822. """Stop the currently executing process."""
  823. _goto_mode(MODE_SERVICE)
  824. _input("service_stop")
  825. _handle_output("Success")
  826. global mode
  827. mode = MODE_MODELLING
  828. def service_get(task):
  829. """Get the values on the specified task."""
  830. _goto_mode(MODE_SERVICE)
  831. val = _output(task=task)
  832. return val
  833. def service_set(task, value):
  834. """Set a value on a specified task."""
  835. _check_type_list(value)
  836. _goto_mode(MODE_SERVICE)
  837. _input(value, task=task)
  838. def user_password(user, password):
  839. """Change a user's password."""
  840. raise NotImplementedError()
  841. def transformation_read_signature(transformation):
  842. """Reads an operation's signature, specifying the names and their required types."""
  843. raise NotImplementedError()
  844. def element_list_nice(model_name):
  845. """Fetches a nice representation of models."""
  846. _goto_mode(MODE_MODELLING)
  847. _input(["element_list_nice", model_name, _get_metamodel(model_name)])
  848. data = _handle_output("Success: ", split=True)[0]
  849. try:
  850. return json.loads(data)
  851. except:
  852. print(data)
  853. raise
  854. def connections_between(model_name, source_element, target_element):
  855. """Gets a list of all allowed connections between the source and target element in the model."""
  856. _goto_mode(MODE_MODIFY, model_name)
  857. _input(["connections_between", source_element, target_element])
  858. output = _handle_output("Success: ", split=True)
  859. return set(output)
  860. def define_attribute(model_name, node, attr_name, attr_type):
  861. """Create a new attribute, which can be instantiated one meta-level below."""
  862. _goto_mode(MODE_MODIFY, model_name)
  863. _input(["define_attribute", node, attr_name, attr_type])
  864. return _handle_output("Success: ", split=True)[0]
  865. def all_instances(model_name, type_name):
  866. """Returns a list of all elements of a specific type."""
  867. _goto_mode(MODE_MODIFY, model_name)
  868. _input(["all_instances", type_name])
  869. output = _handle_output("Success: ", split=True)
  870. return set(output)
  871. def service_poll(port):
  872. """Checks whether or not the Modelverse side has any input ready to be processed."""
  873. raise NotImplementedError()
  874. def user_name(user, username):
  875. """Change a user's name."""
  876. raise NotImplementedError()
  877. def remove_conformance(model_name, metamodel_name):
  878. """Remove a metamodel for a model."""
  879. _goto_mode(MODE_MODELLING)
  880. _input(["remove_conformance", model_name, metamodel_name])
  881. _handle_output("Success")
  882. def add_conformance(model_name, metamodel_name, partial_type_mapping=None):
  883. """Add a metamodel for a model."""
  884. raise NotImplementedError()
  885. _goto_mode(MODE_MODELLING)
  886. _input(["add_conformance", model_name, metamodel_name])
  887. _handle_output("Success")
  888. def folder_create(folder_name):
  889. _goto_mode(MODE_MODELLING)
  890. _input(["folder_create", folder_name])
  891. _handle_output("Success")