modelverse.py 32 KB

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