modelverse.py 33 KB

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