import urllib import urllib2 import json import random from urllib2 import URLError import sys import time import threading COMPILER_PATH = "interface/HUTN" MODE_UNCONNECTED = 0 MODE_UNAUTHORIZED = 1 MODE_MODELLING = 2 MODE_SERVICE = 6 # Bind to the compiler (might have to update path manually!) sys.path.append(COMPILER_PATH) from hutn_compiler.compiler import main as do_compile # Exceptions class ModelverseException(Exception): pass class UnknownError(ModelverseException): pass class UnknownIdentifier(ModelverseException): pass class CompilationError(ModelverseException): pass class NoSuchAttribute(ModelverseException): pass class UnknownModel(ModelverseException): pass class ConnectionError(ModelverseException): pass class ModelExists(ModelverseException): pass class PermissionDenied(ModelverseException): pass class InvalidMode(ModelverseException): pass class InterfaceMismatch(ModelverseException): pass class UnknownMetamodellingHierarchy(ModelverseException): pass # Helper functions and configuration: do not use yourself! taskname = None address = None last_output = None mode = MODE_UNCONNECTED prev_mode = None current_model = None registered_metamodels = {} def _check_type(value): if not isinstance(value, (int, long, float, str, unicode, bool)): raise UnsupportedValue("%s : %s" % (value, str(type(value)))) def _check_type_list(value): if isinstance(value, list): [_check_type(i) for i in value] else: _check_type(value) def _goto_mode(new_mode, model_name=None): global mode if mode == new_mode: return else: # Go to a mode that we have no automatic transfer to: raise exception raise InvalidMode("Required mode: %s, current mode: %s" % (new_mode, mode)) def _input(value, port=None): # Ugly json encoding of primitives if port is None: port = taskname if isinstance(value, type([])): value = json.dumps(value) urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": value, "taskname": port}))).read() else: value = json.dumps(value) #print("Set input: " + str(value)) urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": port}))).read() def _input_raw(value, taskname): # Ugly json encoding of primitives urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read() def _compile_AL(code): # Compile an action language file and send the compiled code code_fragments = code.split("\n") code_fragments = [i for i in code_fragments if i.strip() != ""] code_fragments = [i.replace(" ", "\t") for i in code_fragments] initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments]) code_fragments = [i[initial_tabs:] for i in code_fragments] code_fragments.append("") code = "\n".join(code_fragments) with open(".code.alc", "w") as f: f.write(code) f.flush() compiled = do_compile(".code.alc", COMPILER_PATH + "/grammars/actionlanguage.g", "CS") return compiled def _compile_model(code): # Compile a model and send the compiled graph # First change multiple spaces to a tab code_fragments = code.split("\n") code_fragments = [i for i in code_fragments if i.strip() != ""] code_fragments = [i.replace(" ", "\t") for i in code_fragments] initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments]) code_fragments = [i[initial_tabs:] for i in code_fragments] code_fragments.append("") code = "\n".join(code_fragments) with open(".model.mvc", "w") as f: f.write(code) f.flush() return do_compile(".model.mvc", COMPILER_PATH + "/grammars/modelling.g", "M") def _output(expected=None,port=None): if port is None: port = taskname try: global last_output last_output = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": port}))).read()) #print("[OUT] %s" % last_output) except: raise UnknownError() if expected is not None and last_output != expected: raise InterfaceMismatch(_last_output(), expected) return last_output def _last_output(): return last_output # Raise common exceptions def _handle_output(requested=None, split=None): value = _output() if value.startswith("Model exists: "): raise ModelExists(value.split(": ", 1)[1]) elif value.startswith("Permission denied"): raise PermissionDenied(value.split(": ", 1)[1]) elif value.startswith("Model not found: "): raise UnknownModel(value.split(": ", 1)[1]) elif value.startswith("Element not found: "): raise UnknownIdentifier(value.split(": ", 1)[1]) elif value.startswith("Element exists: "): raise ElementExists(value.split(": ", 1)[1]) elif value.startswith("Attribute not found: "): raise NoSuchAttribute(value.split(": ", 1)[1]) elif requested is not None and value.startswith(requested): if split is None: return value else: splitted = value.strip().split(split, 1) if len(splitted) == 1: return "" else: return splitted[1].rstrip() else: raise InterfaceMismatch(value) def _model_modify(model_name, metamodel_name): """Modify an existing model.""" global mode global prev_mode if mode == MODE_MANUAL: prev_mode = MODE_MANUAL mode = MODE_MODIFY return None _goto_mode(MODE_MODELLING) prev_mode = MODE_MODELLING _input(["model_modify", model_name, metamodel_name]) _handle_output("Success") global current_model current_model = model_name # Mode has changed mode = MODE_MODIFY _output("Model loaded, ready for commands!") def _model_exit(): """Leave model modify mode.""" global mode global prev_mode if prev_mode == MODE_MANUAL: mode = MODE_MANUAL return if mode != MODE_MODIFY: raise InvalidMode() _input("exit") _output("Success") mode = MODE_MODELLING def alter_context(model_name, metamodel_name): global registered_metamodels registered_metamodels[model_name] = metamodel_name # Main MvC operations def init(address_param="127.0.0.1:8001", timeout=20.0): """Starts up the connection to the Modelverse.""" global mode global address global taskname address = "http://%s" % address_param start_time = time.time() taskname = random.random() while 1: try: _input_raw('"%s"' % taskname, "task_manager") mode = MODE_UNAUTHORIZED break except URLError as e: if time.time() - start_time > timeout: raise ConnectionError(e.reason) else: time.sleep(0.1) def login(username, password): """Log in a user, if user doesn't exist, it is created.""" global mode _goto_mode(MODE_UNAUTHORIZED) _output("Log on as which user?") _input(username) if _output() == "Password for existing user?": _input(password) if _output() == "Welcome to the Model Management Interface v2.0!": _output("Use the 'help' command for a list of possible commands") _input("quiet") mode = MODE_MODELLING elif _last_output() == "Wrong password!": raise PermissionDenied() else: raise InterfaceMismatch(_last_output()) elif _last_output() == "This is a new user: please give password!": _input(password) _output("Please repeat the password") _input(password) if _output() == "Passwords match!": _output("Welcome to the Model Management Interface v2.0!") _output("Use the 'help' command for a list of possible commands") _input("quiet") mode = MODE_MODELLING elif _last_output() == "Not the same password!": # We just sent the same password, so it should be identical, unless the interface changed raise InterfaceMismatch(_last_output()) else: raise InterfaceMismatch(_last_output()) else: raise InterfaceMismatch(_last_output()) def service_register(name, function): """Register a function as a service with a specific name.""" def service_process(port): while 1: thrd = threading.Thread(target=function, args=[service_get(port)]) thrd.daemon = True thrd.start() global mode _goto_mode(MODE_MODELLING) _input(["service_register", name]) # Now we are in service-mode mode = MODE_SERVICE port = _handle_output("Success: ", split=" ") # Process events in the background! threading.Thread(target=service_process, args=[port]).start() def service_stop(): """Stop the currently executing process.""" _goto_mode(MODE_SERVICE) _input("service_stop") _handle_output("Success") global mode mode = MODE_MODELLING def service_get(port): """Get the values on the specified port.""" _goto_mode(MODE_SERVICE) return _output(port=port) def service_set(port, value): """Set a value on a specified port.""" _check_type_list(value) _goto_mode(MODE_SERVICE) _input(value, port=port) def service_poll(port): """Checks whether or not the Modelverse side has any input ready to be processed.""" raise NotImplementedError()