modelverse.py 34 KB

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