modelverse.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. from sccd.runtime.statecharts_core import Event
  2. import sccd.runtime.socket2event as socket2event
  3. import modelverse_SCCD
  4. import time
  5. import threading
  6. # Exceptions
  7. class ModelverseException(Exception):
  8. pass
  9. class UnknownError(ModelverseException):
  10. pass
  11. class UnknownIdentifier(ModelverseException):
  12. pass
  13. class CompilationError(ModelverseException):
  14. pass
  15. class NoSuchAttribute(ModelverseException):
  16. pass
  17. class UnknownModel(ModelverseException):
  18. pass
  19. class ConnectionError(ModelverseException):
  20. pass
  21. class ModelExists(ModelverseException):
  22. pass
  23. class PermissionDenied(ModelverseException):
  24. pass
  25. class InvalidMode(ModelverseException):
  26. pass
  27. class InterfaceMismatch(ModelverseException):
  28. pass
  29. class UnknownMetamodellingHierarchy(ModelverseException):
  30. pass
  31. def run_controller():
  32. try:
  33. controller.start()
  34. finally:
  35. controller.stop()
  36. def _next_ID():
  37. global ID
  38. ID += 1
  39. return ID
  40. def __run_new_modelverse(address, username, password, callback, model):
  41. print("RUN NEW")
  42. init(address)
  43. login(username, password)
  44. print("LOGIN OK")
  45. if callback is not None:
  46. callback(model)
  47. exit_save(model)
  48. print("CALLBACK DONE")
  49. def __run_new_modelverse_activity(address, username, password, taskname, pipe, callback):
  50. print("Run MV activity")
  51. init(address, taskname=taskname)
  52. controller.username = username
  53. controller.password = password
  54. print("INIT OK")
  55. t = OUTPUT()
  56. print("Got type: " + str(t))
  57. if t == "OP":
  58. print("Requesting model...")
  59. model = OUTPUT()
  60. print("Do manual operations on " + str(model))
  61. __invoke(callback, model)
  62. print("Invocation OK")
  63. controller.addInput(Event("data_input", "action_in", [None, None]))
  64. print("Waiting on output")
  65. # TODO fix this to something using OUTPUT or so
  66. time.sleep(5)
  67. elif t == "SC":
  68. while 1:
  69. empty = True
  70. # Fetch output from the MV
  71. response = responses.fetch(0)
  72. if response is not None:
  73. print("Output of MV to SC: " + str(response))
  74. if response.name == "data_output":
  75. # Got output of MV, so forward to SCCD
  76. pipe.send(("input", response.parameters))
  77. elif response.name == "result":
  78. # Finished execution, so continue and return result
  79. pipe.send(("terminate", []))
  80. pipe.close()
  81. return response.parameters[1]
  82. else:
  83. raise Exception("Unknown data from MV to SC: " + str(response))
  84. empty = False
  85. # Fetch output from the SC
  86. if pipe.poll():
  87. response = pipe.recv()
  88. if response.name == "output":
  89. controller.addInput(Event("data_input", "action_in", [response.parameters, context]))
  90. else:
  91. raise Exception("Unknown data from SC to MV: " + str(response))
  92. empty = False
  93. if empty:
  94. time.sleep(0.05)
  95. def __invoke(callback, model):
  96. import multiprocessing
  97. print("Invoked action!")
  98. p = multiprocessing.Process(target=__run_new_modelverse, args=[controller.address, controller.username, controller.password, callback, model])
  99. p.start()
  100. p.join()
  101. print("Invocation done")
  102. def _process_SC(statechart, port_sc, taskname):
  103. import multiprocessing
  104. p2c_pipe, c2p_pipe = multiprocessing.Pipe()
  105. p = multiprocessing.Process(target=__run_new_modelverse_activity, args=[controller.address, controller.username, controller.password, taskname, c2p_pipe, None])
  106. p.start()
  107. while 1:
  108. empty = True
  109. if p2c_pipe.poll():
  110. response = p2c_pipe.recv()
  111. statechart[0].addInput(Event(response[0], statechart[1], response[1]))
  112. if response[0] == "terminate":
  113. p2c_pipe.close()
  114. break
  115. empty = False
  116. response = port_sc.fetch(0)
  117. if response is not None:
  118. p2c_pipe.send(response)
  119. empty = False
  120. if empty:
  121. time.sleep(0.05)
  122. p.join()
  123. def _process_OP(callback, taskname):
  124. import multiprocessing
  125. print("Running remote operation for taskname " + taskname)
  126. p = multiprocessing.Process(target=__run_new_modelverse_activity, args=[controller.address, controller.username, controller.password, taskname, None, callback])
  127. p.start()
  128. p.join()
  129. def INPUT(action, context, parameters):
  130. controller.addInput(Event("action", "action_in", [action, _next_ID(), context, parameters]))
  131. def OUTPUT():
  132. while 1:
  133. response = responses.fetch(-1)
  134. if response.name == "result":
  135. return response.parameters[1]
  136. elif response.name == "exception":
  137. if response.parameters[1] == "UnknownIdentifier":
  138. raise UnknownIdentifier()
  139. elif response.parameters[1] == "UnknownMetamodellingHierarchy":
  140. raise UnknownMetamodellingHierarchy()
  141. else:
  142. print("Unknown error: " + str(response.parameters))
  143. raise UnknownError()
  144. def init(address_param="127.0.0.1:8001", timeout=20.0, taskname=None):
  145. global controller
  146. global ID
  147. global responses
  148. controller = modelverse_SCCD.Controller(taskname)
  149. socket2event.boot_translation_service(controller)
  150. ID = 0
  151. thrd = threading.Thread(target=run_controller)
  152. thrd.daemon = True
  153. thrd.start()
  154. responses = controller.addOutputListener("action_out")
  155. controller.addOutputListener("ready").fetch(-1)
  156. INPUT("init", None, [address_param, timeout])
  157. controller.address = address_param
  158. return OUTPUT()
  159. def login(username, password):
  160. controller.username = username
  161. controller.password = password
  162. INPUT("login", None, [username, password])
  163. return OUTPUT()
  164. def model_list(location):
  165. INPUT("model_list", None, [location])
  166. return OUTPUT()
  167. def model_add(model_name, metamodel_name, model_code=""):
  168. INPUT("model_add", None, [model_name, metamodel_name, model_code])
  169. return OUTPUT()
  170. def model_delete(model_name):
  171. INPUT("model_delete", None, [model_name])
  172. return OUTPUT()
  173. def model_list_full(location):
  174. INPUT("model_list_full", None, [location])
  175. return OUTPUT()
  176. def verify(model_name, metamodel_name):
  177. INPUT("verify", None, [model_name, metamodel_name])
  178. return OUTPUT()
  179. def model_overwrite(model_name, new_model, context=None):
  180. INPUT("model_overwrite", context, [model_name, new_model])
  181. return OUTPUT()
  182. def disconnect():
  183. INPUT("disconnect", None, [])
  184. return OUTPUT()
  185. def user_logout():
  186. INPUT("user_logout", None, [])
  187. return OUTPUT()
  188. def user_delete():
  189. INPUT("user_delete", None, [])
  190. return OUTPUT()
  191. def model_render(model_name, mapper_name, rendered_name):
  192. INPUT("model_render", None, [model_name, mapper_name, rendered_name])
  193. return OUTPUT()
  194. def transformation_between(sources, targets):
  195. INPUT("transformation_between", None, [source, target])
  196. return OUTPUT()
  197. def transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=None):
  198. INPUT("transformation_add_MT", None, [source_metamodels, target_metamodels, operation_name, code, True])
  199. model = OUTPUT()
  200. if callback is not None:
  201. __invoke(callback, model)
  202. controller.addInput(Event("data_input", "action_in", [None, None]))
  203. return OUTPUT()
  204. def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=None):
  205. INPUT("transformation_add_AL", None, [source_metamodels, target_metamodels, operation_name, code, True])
  206. model = OUTPUT()
  207. if model is None:
  208. # In case the source and target metamodels are empty, the context will be None, indicating that we are finished already (no callbacks allowed)
  209. return
  210. if callback is not None:
  211. __invoke(callback, model)
  212. controller.addInput(Event("data_input", "action_in", [None, None]))
  213. return OUTPUT()
  214. def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=None):
  215. INPUT("transformation_add_MANUAL", None, [source_metamodels, target_metamodels, operation_name, True])
  216. model = OUTPUT()
  217. if callback is not None:
  218. __invoke(callback, model)
  219. controller.addInput(Event("data_input", "action_in", [None, None]))
  220. return OUTPUT()
  221. def __transformation_execute(operation_name, input_models_dict, output_models_dict, statechart, tracability_model, fetch_output):
  222. if statechart is not None:
  223. port_sc = statechart[0].addOutputListener(statechart[2])
  224. INPUT("transformation_execute", None, [operation_name, input_models_dict, output_models_dict, tracability_model, fetch_output])
  225. taskname = OUTPUT()
  226. if statechart is not None:
  227. threading.Thread(target=_process_SC, args=[statechart, port_sc, taskname]).start()
  228. return OUTPUT()
  229. def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, statechart=None, tracability_model="", fetch_output=True):
  230. return __transformation_execute(operation_name, input_models_dict, output_models_dict, statechart, tracability_model, fetch_output)
  231. def transformation_execute_AL(operation_name, input_models_dict, output_models_dict, statechart=None, tracability_model="", fetch_output=True):
  232. return __transformation_execute(operation_name, input_models_dict, output_models_dict, statechart, tracability_model, fetch_output)
  233. def transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=None, tracability_model=""):
  234. INPUT("transformation_execute", None, [operation_name, input_models_dict, output_models_dict, tracability_model])
  235. taskname = OUTPUT()
  236. print("Running manual task at " + str(taskname))
  237. _process_OP(callback, taskname)
  238. print("Process OP called")
  239. return OUTPUT()
  240. def transformation_signature(operation_name):
  241. INPUT("transformation_signature", None, [operation_name])
  242. return OUTPUT()
  243. def process_signature(process_name):
  244. INPUT("process_signature", None, [operation_name])
  245. return OUTPUT()
  246. def permission_modify(model_name, permissions):
  247. INPUT("permission_modify", None, [model_name, permissions])
  248. return OUTPUT()
  249. def permission_owner(model_name, permission):
  250. INPUT("permission_owner", None, [model_name, permission])
  251. return OUTPUT()
  252. def permission_group(model_name, group):
  253. INPUT("permission_group", None, [model_name, group])
  254. return OUTPUT()
  255. def group_create(group_name):
  256. INPUT("group_create", None, [group_name])
  257. return OUTPUT()
  258. def group_delete(group_name):
  259. INPUT("group_delete", None, [group_name])
  260. return OUTPUT()
  261. def group_owner_add(group_name, user_name):
  262. INPUT("group_owner_add", None, [group_name, user_name])
  263. return OUTPUT()
  264. def group_owner_delete(group_name, user_name):
  265. INPUT("group_owner_delete", None, [group_name, user_name])
  266. return OUTPUT()
  267. def group_join(group_name, user_name):
  268. INPUT("group_join", None, [group_name, user_name])
  269. return OUTPUT()
  270. def group_kick(group_name, user_name):
  271. INPUT("group_kick", None, [group_name, user_name])
  272. return OUTPUT()
  273. def group_list():
  274. INPUT("group_list", None, [])
  275. return OUTPUT()
  276. def admin_promote(user_name):
  277. INPUT("admin_promote", None, [user_name])
  278. return OUTPUT()
  279. def admin_demote(user_name):
  280. INPUT("admin_demote", None, [user_name])
  281. return OUTPUT()
  282. def conformance_delete(model_name, metamodel_name):
  283. INPUT("conformance_delete", None, [model_name, metamodel_name])
  284. return OUTPUT()
  285. def conformance_add(model_name, metamodel_name):
  286. INPUT("conformance_add", None, [model_name, metamodel_name])
  287. return OUTPUT()
  288. def folder_create(folder_name):
  289. INPUT("folder_create", None, [folder_name])
  290. return OUTPUT()
  291. def model_types(model_name):
  292. INPUT("model_types", None, [model_name])
  293. return OUTPUT()
  294. def alter_context(model_name, metamodel_name):
  295. INPUT("alter_context", None, [model_name, metamodel_name])
  296. def element_list(model_name, context=None):
  297. INPUT("element_list", context, [model_name])
  298. return OUTPUT()
  299. def element_list_nice(model_name, context=None):
  300. INPUT("element_list_nice", context, [model_name])
  301. return OUTPUT()
  302. def types(model_name, context=None):
  303. INPUT("types", context, [model_name])
  304. return OUTPUT()
  305. def types_full(model_name, context=None):
  306. INPUT("types_full", context, [model_name])
  307. return OUTPUT()
  308. def read_info(model_name, ID, context=None):
  309. INPUT("read_info", context, [model_name, ID])
  310. return OUTPUT()
  311. def read_attrs(model_name, ID, context=None):
  312. INPUT("read_attrs", context, [model_name, ID])
  313. return OUTPUT()
  314. def instantiate(model_name, typename, edge=None, ID="", context=None):
  315. INPUT("instantiate", context, [model_name, typename, edge, ID])
  316. return OUTPUT()
  317. def delete_element(model_name, ID, context=None):
  318. INPUT("delete_element", context, [model_name, ID])
  319. return OUTPUT()
  320. def attr_assign(model_name, ID, attr, value, context=None):
  321. INPUT("attr_assign", context, [model_name, ID, attr, value])
  322. return OUTPUT()
  323. def attr_assign_code(model_name, ID, attr, code, context=None):
  324. INPUT("attr_assign_code", context, [model_name, ID, attr, code])
  325. return OUTPUT()
  326. def attr_delete(model_name, ID, attr, context=None):
  327. INPUT("attr_delete", context, [model_name, ID, attr])
  328. return OUTPUT()
  329. def read_outgoing(model_name, ID, typename, context=None):
  330. INPUT("read_outgoing", context, [model_name, ID, typename])
  331. return OUTPUT()
  332. def read_incoming(model_name, ID, typename, context=None):
  333. INPUT("read_incoming", context, [model_name, ID, typename])
  334. return OUTPUT()
  335. def read_association_source(model_name, ID, context=None):
  336. INPUT("read_association_source", context, [model_name, ID])
  337. return OUTPUT()
  338. def read_association_destination(model_name, ID, context=None):
  339. INPUT("read_association_destination", context, [model_name, ID])
  340. return OUTPUT()
  341. def connections_between(model_name, source, target, context=None):
  342. INPUT("connections_between", context, [model_name, source, target])
  343. return OUTPUT()
  344. def define_attribute(model_name, node, attr_name, attr_type, context=None):
  345. INPUT("define_attribute", context, [model_name, node, attr_name, attr_type])
  346. return OUTPUT()
  347. def all_instances(model_name, type_name, context=None):
  348. INPUT("all_instances", context, [model_name, type_name])
  349. return OUTPUT()
  350. def process_execute(process_name, prefix, callbacks=None):
  351. # for all callbacks to SCs, start up the output port already
  352. sc_ports = {}
  353. for k, v in callbacks.items():
  354. if isinstance(v, (tuple, list)):
  355. # Is a statechart, so register already
  356. sc_ports[k] = v[0].addOutputListener(v[2])
  357. INPUT("process_execute", None, [process_name, prefix])
  358. operation = OUTPUT()
  359. while 1:
  360. if isinstance(operation, (list, tuple)):
  361. if operation[0] == "OP":
  362. t, name, context, model = operation
  363. if name in callbacks:
  364. callbacks[name](context)
  365. INPUT("exit", context, [])
  366. operation = OUTPUT()
  367. elif operation[0] == "SC":
  368. t, name, context = operation
  369. if name in callbacks:
  370. statechart = callbacks[name]
  371. else:
  372. statechart = None
  373. while 1:
  374. empty = True
  375. # Fetch output from the MV
  376. response = responses.fetch(0)
  377. if response is not None:
  378. if response.name == "data_output":
  379. # Got output of MV, so forward to SCCD
  380. if statechart:
  381. statechart[0].addInput(Event("input", statechart[1], response.parameters))
  382. elif response.name == "result":
  383. # Finished execution, so continue and return result
  384. if statechart:
  385. statechart[0].addInput(Event("terminate", statechart[1], []))
  386. # Break from the most inner loop
  387. operation = response.parameters[1]
  388. break
  389. empty = False
  390. # Fetch output from the SC
  391. if statechart:
  392. response = sc_ports[name].fetch(0)
  393. if response is not None:
  394. if response.name == "output":
  395. controller.addInput(Event("data_input", "action_in", [response.parameters, context]))
  396. empty = False
  397. if empty:
  398. time.sleep(0.01)
  399. else:
  400. if operation == "Finished":
  401. # Finished execution of the process, so exit
  402. return None
  403. def get_taskname():
  404. """Fetch the taskname of the current connection."""
  405. return controller.taskname
  406. def exit_save(model_name):
  407. INPUT("exit_save", None, [model_name])
  408. return OUTPUT()
  409. """ Some hardcoded functions... Way easier to express them with code than with statecharts!"""
  410. import json
  411. import urllib
  412. import urllib2
  413. def service_register(name, function):
  414. """Register a function as a service with a specific name."""
  415. INPUT("service_register", None, [name, function])
  416. port = OUTPUT()
  417. return port
  418. def service_stop():
  419. """Stop the currently executing process."""
  420. INPUT("service_stop", None, [])
  421. return OUTPUT()
  422. def service_get(port):
  423. """Get the values on the specified port."""
  424. val = json.loads(urllib2.urlopen(urllib2.Request("http://%s" % controller.address, urllib.urlencode({"op": "get_output", "taskname": port}))).read())
  425. return val
  426. def service_set(port, value):
  427. """Set a value on a specified port."""
  428. if isinstance(value, type([])):
  429. value = json.dumps(value)
  430. urllib2.urlopen(urllib2.Request("http://%s" % controller.address, urllib.urlencode({"op": "set_input", "data": value, "taskname": port}))).read()
  431. else:
  432. value = json.dumps(value)
  433. urllib2.urlopen(urllib2.Request("http://%s" % controller.address, urllib.urlencode({"op": "set_input", "value": value, "taskname": port}))).read()
  434. def service_poll(port):
  435. """Checks whether or not the Modelverse side has any input ready to be processed."""
  436. raise NotImplementedError()