modelverse.py 32 KB

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