modelverse_coded.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. COMPILER_PATH = "interface/HUTN"
  10. MODE_UNCONNECTED = 0
  11. MODE_UNAUTHORIZED = 1
  12. MODE_MODELLING = 2
  13. MODE_SERVICE = 6
  14. # Bind to the compiler (might have to update path manually!)
  15. sys.path.append(COMPILER_PATH)
  16. from hutn_compiler.compiler import main as do_compile
  17. # Exceptions
  18. class ModelverseException(Exception):
  19. pass
  20. class UnknownError(ModelverseException):
  21. pass
  22. class UnknownIdentifier(ModelverseException):
  23. pass
  24. class CompilationError(ModelverseException):
  25. pass
  26. class NoSuchAttribute(ModelverseException):
  27. pass
  28. class UnknownModel(ModelverseException):
  29. pass
  30. class ConnectionError(ModelverseException):
  31. pass
  32. class ModelExists(ModelverseException):
  33. pass
  34. class PermissionDenied(ModelverseException):
  35. pass
  36. class InvalidMode(ModelverseException):
  37. pass
  38. class InterfaceMismatch(ModelverseException):
  39. pass
  40. class UnknownMetamodellingHierarchy(ModelverseException):
  41. pass
  42. # Helper functions and configuration: do not use yourself!
  43. taskname = None
  44. address = None
  45. last_output = None
  46. mode = MODE_UNCONNECTED
  47. prev_mode = None
  48. current_model = None
  49. registered_metamodels = {}
  50. def _check_type(value):
  51. if not isinstance(value, (int, long, float, str, unicode, bool)):
  52. raise UnsupportedValue("%s : %s" % (value, str(type(value))))
  53. def _check_type_list(value):
  54. if isinstance(value, list):
  55. [_check_type(i) for i in value]
  56. else:
  57. _check_type(value)
  58. def _goto_mode(new_mode, model_name=None):
  59. global mode
  60. if mode == new_mode:
  61. return
  62. else:
  63. # Go to a mode that we have no automatic transfer to: raise exception
  64. raise InvalidMode("Required mode: %s, current mode: %s" % (new_mode, mode))
  65. def _input(value, port=None):
  66. # Ugly json encoding of primitives
  67. if port is None:
  68. port = taskname
  69. if isinstance(value, type([])):
  70. value = json.dumps(value)
  71. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": value, "taskname": port}))).read()
  72. else:
  73. value = json.dumps(value)
  74. #print("Set input: " + str(value))
  75. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": port}))).read()
  76. def _input_raw(value, taskname):
  77. # Ugly json encoding of primitives
  78. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
  79. def _compile_AL(code):
  80. # Compile an action language file and send the compiled code
  81. code_fragments = code.split("\n")
  82. code_fragments = [i for i in code_fragments if i.strip() != ""]
  83. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  84. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  85. code_fragments = [i[initial_tabs:] for i in code_fragments]
  86. code_fragments.append("")
  87. code = "\n".join(code_fragments)
  88. with open(".code.alc", "w") as f:
  89. f.write(code)
  90. f.flush()
  91. compiled = do_compile(".code.alc", COMPILER_PATH + "/grammars/actionlanguage.g", "CS")
  92. return compiled
  93. def _compile_model(code):
  94. # Compile a model and send the compiled graph
  95. # First change multiple spaces to a tab
  96. code_fragments = code.split("\n")
  97. code_fragments = [i for i in code_fragments if i.strip() != ""]
  98. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  99. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  100. code_fragments = [i[initial_tabs:] for i in code_fragments]
  101. code_fragments.append("")
  102. code = "\n".join(code_fragments)
  103. with open(".model.mvc", "w") as f:
  104. f.write(code)
  105. f.flush()
  106. return do_compile(".model.mvc", COMPILER_PATH + "/grammars/modelling.g", "M")
  107. def _output(expected=None,port=None):
  108. if port is None:
  109. port = taskname
  110. try:
  111. global last_output
  112. last_output = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": port}))).read())
  113. #print("[OUT] %s" % last_output)
  114. except:
  115. raise UnknownError()
  116. if expected is not None and last_output != expected:
  117. raise InterfaceMismatch(_last_output(), expected)
  118. return last_output
  119. def _last_output():
  120. return last_output
  121. # Raise common exceptions
  122. def _handle_output(requested=None, split=None):
  123. value = _output()
  124. if value.startswith("Model exists: "):
  125. raise ModelExists(value.split(": ", 1)[1])
  126. elif value.startswith("Permission denied"):
  127. raise PermissionDenied(value.split(": ", 1)[1])
  128. elif value.startswith("Model not found: "):
  129. raise UnknownModel(value.split(": ", 1)[1])
  130. elif value.startswith("Element not found: "):
  131. raise UnknownIdentifier(value.split(": ", 1)[1])
  132. elif value.startswith("Element exists: "):
  133. raise ElementExists(value.split(": ", 1)[1])
  134. elif value.startswith("Attribute not found: "):
  135. raise NoSuchAttribute(value.split(": ", 1)[1])
  136. elif requested is not None and value.startswith(requested):
  137. if split is None:
  138. return value
  139. else:
  140. splitted = value.strip().split(split, 1)
  141. if len(splitted) == 1:
  142. return ""
  143. else:
  144. return splitted[1].rstrip()
  145. else:
  146. raise InterfaceMismatch(value)
  147. def _model_modify(model_name, metamodel_name):
  148. """Modify an existing model."""
  149. global mode
  150. global prev_mode
  151. if mode == MODE_MANUAL:
  152. prev_mode = MODE_MANUAL
  153. mode = MODE_MODIFY
  154. return None
  155. _goto_mode(MODE_MODELLING)
  156. prev_mode = MODE_MODELLING
  157. _input(["model_modify", model_name, metamodel_name])
  158. _handle_output("Success")
  159. global current_model
  160. current_model = model_name
  161. # Mode has changed
  162. mode = MODE_MODIFY
  163. _output("Model loaded, ready for commands!")
  164. def _model_exit():
  165. """Leave model modify mode."""
  166. global mode
  167. global prev_mode
  168. if prev_mode == MODE_MANUAL:
  169. mode = MODE_MANUAL
  170. return
  171. if mode != MODE_MODIFY:
  172. raise InvalidMode()
  173. _input("exit")
  174. _output("Success")
  175. mode = MODE_MODELLING
  176. def alter_context(model_name, metamodel_name):
  177. global registered_metamodels
  178. registered_metamodels[model_name] = metamodel_name
  179. # Main MvC operations
  180. def init(address_param="127.0.0.1:8001", timeout=20.0):
  181. """Starts up the connection to the Modelverse."""
  182. global mode
  183. global address
  184. global taskname
  185. address = "http://%s" % address_param
  186. start_time = time.time()
  187. taskname = random.random()
  188. while 1:
  189. try:
  190. _input_raw('"%s"' % taskname, "task_manager")
  191. mode = MODE_UNAUTHORIZED
  192. break
  193. except URLError as e:
  194. if time.time() - start_time > timeout:
  195. raise ConnectionError(e.reason)
  196. else:
  197. time.sleep(0.1)
  198. def login(username, password):
  199. """Log in a user, if user doesn't exist, it is created."""
  200. global mode
  201. _goto_mode(MODE_UNAUTHORIZED)
  202. _output("Log on as which user?")
  203. _input(username)
  204. if _output() == "Password for existing user?":
  205. _input(password)
  206. if _output() == "Welcome to the Model Management Interface v2.0!":
  207. _output("Use the 'help' command for a list of possible commands")
  208. _input("quiet")
  209. mode = MODE_MODELLING
  210. elif _last_output() == "Wrong password!":
  211. raise PermissionDenied()
  212. else:
  213. raise InterfaceMismatch(_last_output())
  214. elif _last_output() == "This is a new user: please give password!":
  215. _input(password)
  216. _output("Please repeat the password")
  217. _input(password)
  218. if _output() == "Passwords match!":
  219. _output("Welcome to the Model Management Interface v2.0!")
  220. _output("Use the 'help' command for a list of possible commands")
  221. _input("quiet")
  222. mode = MODE_MODELLING
  223. elif _last_output() == "Not the same password!":
  224. # We just sent the same password, so it should be identical, unless the interface changed
  225. raise InterfaceMismatch(_last_output())
  226. else:
  227. raise InterfaceMismatch(_last_output())
  228. else:
  229. raise InterfaceMismatch(_last_output())
  230. def service_register(name, function):
  231. """Register a function as a service with a specific name."""
  232. def service_process(port):
  233. while 1:
  234. thrd = threading.Thread(target=function, args=[service_get(port)])
  235. thrd.daemon = True
  236. thrd.start()
  237. global mode
  238. _goto_mode(MODE_MODELLING)
  239. _input(["service_register", name])
  240. # Now we are in service-mode
  241. mode = MODE_SERVICE
  242. port = _handle_output("Success: ", split=" ")
  243. # Process events in the background!
  244. threading.Thread(target=service_process, args=[port]).start()
  245. def service_stop():
  246. """Stop the currently executing process."""
  247. _goto_mode(MODE_SERVICE)
  248. _input("service_stop")
  249. _handle_output("Success")
  250. global mode
  251. mode = MODE_MODELLING
  252. def service_get(port):
  253. """Get the values on the specified port."""
  254. _goto_mode(MODE_SERVICE)
  255. return _output(port=port)
  256. def service_set(port, value):
  257. """Set a value on a specified port."""
  258. _check_type_list(value)
  259. _goto_mode(MODE_SERVICE)
  260. _input(value, port=port)
  261. def service_poll(port):
  262. """Checks whether or not the Modelverse side has any input ready to be processed."""
  263. raise NotImplementedError()