modelverse.py 31 KB

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