modelverse_coded.py 32 KB

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