Parcourir la source

Merge branch 'semi_SCCD_client' into testing

Yentl Van Tendeloo il y a 8 ans
Parent
commit
3c13f67f4f

+ 1 - 1
integration/utils.py

@@ -77,6 +77,6 @@ def flush_data(address, data):
 
 def start_mvc():
     port = getFreePort()
-    address = "http://127.0.0.1:%s" % port
+    address = "127.0.0.1:%s" % port
     proc = execute("run_local_modelverse", [str(port)], wait=False)
     return proc, address

+ 1 - 1
scripts/prompt.py

@@ -31,7 +31,7 @@ local_print("Welcome to the local shell!")
 try:
     address = sys.argv[1]
 except IndexError:
-    address = "http://127.0.0.1:8001"
+    address = "127.0.0.1:8001"
 try:
     taskname = sys.argv[2]
 except IndexError:

+ 1 - 1
unit/utils.py

@@ -77,6 +77,6 @@ def flush_data(address, data):
 
 def start_mvc():
     port = getFreePort()
-    address = "http://127.0.0.1:%s" % port
+    address = "127.0.0.1:%s" % port
     proc = execute("run_local_modelverse", [str(port)], wait=False)
     return proc, address

+ 371 - 0
wrappers/http_client.py

@@ -0,0 +1,371 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Date:   Fri Aug 25 15:48:04 2017
+
+Model author: Yentl Van Tendeloo
+Model name:   HTTP client
+Model description:
+HTTP client.
+"""
+
+from sccd.runtime.statecharts_core import *
+import uuid
+
+# package "HTTP client"
+
+class HTTPClient(RuntimeClassBase):
+    def __init__(self, controller):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        HTTPClient.user_defined_constructor(self)
+    
+    def user_defined_constructor(self):
+        self.socket = None
+        self.received_data = ""
+        self.send_data = ""
+        self.queue = []
+        self.IDs = []
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /init
+        self.states["/init"] = State(1, "/init", self)
+        self.states["/init"].setEnter(self._init_enter)
+        
+        # state /waiting
+        self.states["/waiting"] = State(2, "/waiting", self)
+        self.states["/waiting"].setEnter(self._waiting_enter)
+        
+        # state /connecting
+        self.states["/connecting"] = State(3, "/connecting", self)
+        self.states["/connecting"].setEnter(self._connecting_enter)
+        self.states["/connecting"].setExit(self._connecting_exit)
+        
+        # state /connecting/connecting
+        self.states["/connecting/connecting"] = State(4, "/connecting/connecting", self)
+        self.states["/connecting/connecting"].setEnter(self._connecting_connecting_enter)
+        
+        # state /connecting/cooldown
+        self.states["/connecting/cooldown"] = State(5, "/connecting/cooldown", self)
+        self.states["/connecting/cooldown"].setEnter(self._connecting_cooldown_enter)
+        self.states["/connecting/cooldown"].setExit(self._connecting_cooldown_exit)
+        
+        # state /connected
+        self.states["/connected"] = ParallelState(6, "/connected", self)
+        
+        # state /connected/listening
+        self.states["/connected/listening"] = State(7, "/connected/listening", self)
+        
+        # state /connected/listening/listen
+        self.states["/connected/listening/listen"] = State(8, "/connected/listening/listen", self)
+        self.states["/connected/listening/listen"].setEnter(self._connected_listening_listen_enter)
+        
+        # state /connected/listening/close
+        self.states["/connected/listening/close"] = State(9, "/connected/listening/close", self)
+        
+        # state /connected/sending
+        self.states["/connected/sending"] = State(10, "/connected/sending", self)
+        
+        # state /connected/sending/waiting_for_data
+        self.states["/connected/sending/waiting_for_data"] = State(11, "/connected/sending/waiting_for_data", self)
+        
+        # state /connected/sending/transferring
+        self.states["/connected/sending/transferring"] = State(12, "/connected/sending/transferring", self)
+        
+        # state /connected/queueing
+        self.states["/connected/queueing"] = State(13, "/connected/queueing", self)
+        
+        # state /connected/queueing/queueing
+        self.states["/connected/queueing/queueing"] = State(14, "/connected/queueing/queueing", self)
+        self.states["/connected/queueing/queueing"].setEnter(self._connected_queueing_queueing_enter)
+        
+        # state /connected/parsing
+        self.states["/connected/parsing"] = State(15, "/connected/parsing", self)
+        
+        # state /connected/parsing/wait_for_header
+        self.states["/connected/parsing/wait_for_header"] = State(16, "/connected/parsing/wait_for_header", self)
+        
+        # state /connected/parsing/wait_for_payload
+        self.states["/connected/parsing/wait_for_payload"] = State(17, "/connected/parsing/wait_for_payload", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/init"])
+        self.states[""].addChild(self.states["/waiting"])
+        self.states[""].addChild(self.states["/connecting"])
+        self.states[""].addChild(self.states["/connected"])
+        self.states["/connecting"].addChild(self.states["/connecting/connecting"])
+        self.states["/connecting"].addChild(self.states["/connecting/cooldown"])
+        self.states["/connected"].addChild(self.states["/connected/listening"])
+        self.states["/connected"].addChild(self.states["/connected/sending"])
+        self.states["/connected"].addChild(self.states["/connected/queueing"])
+        self.states["/connected"].addChild(self.states["/connected/parsing"])
+        self.states["/connected/listening"].addChild(self.states["/connected/listening/listen"])
+        self.states["/connected/listening"].addChild(self.states["/connected/listening/close"])
+        self.states["/connected/sending"].addChild(self.states["/connected/sending/waiting_for_data"])
+        self.states["/connected/sending"].addChild(self.states["/connected/sending/transferring"])
+        self.states["/connected/queueing"].addChild(self.states["/connected/queueing/queueing"])
+        self.states["/connected/parsing"].addChild(self.states["/connected/parsing/wait_for_header"])
+        self.states["/connected/parsing"].addChild(self.states["/connected/parsing/wait_for_payload"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/init"]
+        self.states["/connecting"].default_state = self.states["/connecting/connecting"]
+        self.states["/connected/listening"].default_state = self.states["/connected/listening/listen"]
+        self.states["/connected/sending"].default_state = self.states["/connected/sending/waiting_for_data"]
+        self.states["/connected/queueing"].default_state = self.states["/connected/queueing/queueing"]
+        self.states["/connected/parsing"].default_state = self.states["/connected/parsing/wait_for_header"]
+        
+        # transition /init
+        _init_0 = Transition(self, self.states["/init"], [self.states["/waiting"]])
+        _init_0.setAction(self._init_0_exec)
+        _init_0.setTrigger(Event("created_socket", "socket_in"))
+        _init_0.setGuard(self._init_0_guard)
+        self.states["/init"].addTransition(_init_0)
+        
+        # transition /waiting
+        _waiting_0 = Transition(self, self.states["/waiting"], [self.states["/connecting"]])
+        _waiting_0.setAction(self._waiting_0_exec)
+        _waiting_0.setTrigger(Event("connect", "request_in"))
+        self.states["/waiting"].addTransition(_waiting_0)
+        
+        # transition /connecting/connecting
+        _connecting_connecting_0 = Transition(self, self.states["/connecting/connecting"], [self.states["/connecting/cooldown"]])
+        _connecting_connecting_0.setTrigger(Event("error_socket", "socket_in"))
+        self.states["/connecting/connecting"].addTransition(_connecting_connecting_0)
+        _connecting_connecting_1 = Transition(self, self.states["/connecting/connecting"], [self.states["/connected"]])
+        _connecting_connecting_1.setAction(self._connecting_connecting_1_exec)
+        _connecting_connecting_1.setTrigger(Event("connected_socket", "socket_in"))
+        _connecting_connecting_1.setGuard(self._connecting_connecting_1_guard)
+        self.states["/connecting/connecting"].addTransition(_connecting_connecting_1)
+        
+        # transition /connecting/cooldown
+        _connecting_cooldown_0 = Transition(self, self.states["/connecting/cooldown"], [self.states["/connecting/connecting"]])
+        _connecting_cooldown_0.setTrigger(Event("_1after"))
+        self.states["/connecting/cooldown"].addTransition(_connecting_cooldown_0)
+        
+        # transition /connected/listening/listen
+        _connected_listening_listen_0 = Transition(self, self.states["/connected/listening/listen"], [self.states["/connected/listening/listen"]])
+        _connected_listening_listen_0.setAction(self._connected_listening_listen_0_exec)
+        _connected_listening_listen_0.setTrigger(Event("received_socket", "socket_in"))
+        _connected_listening_listen_0.setGuard(self._connected_listening_listen_0_guard)
+        self.states["/connected/listening/listen"].addTransition(_connected_listening_listen_0)
+        _connected_listening_listen_1 = Transition(self, self.states["/connected/listening/listen"], [self.states["/connected/listening/close"]])
+        _connected_listening_listen_1.setTrigger(Event("received_socket", "socket_in"))
+        _connected_listening_listen_1.setGuard(self._connected_listening_listen_1_guard)
+        self.states["/connected/listening/listen"].addTransition(_connected_listening_listen_1)
+        
+        # transition /connected/sending/waiting_for_data
+        _connected_sending_waiting_for_data_0 = Transition(self, self.states["/connected/sending/waiting_for_data"], [self.states["/connected/sending/transferring"]])
+        _connected_sending_waiting_for_data_0.setAction(self._connected_sending_waiting_for_data_0_exec)
+        _connected_sending_waiting_for_data_0.setTrigger(None)
+        _connected_sending_waiting_for_data_0.setGuard(self._connected_sending_waiting_for_data_0_guard)
+        self.states["/connected/sending/waiting_for_data"].addTransition(_connected_sending_waiting_for_data_0)
+        
+        # transition /connected/sending/transferring
+        _connected_sending_transferring_0 = Transition(self, self.states["/connected/sending/transferring"], [self.states["/connected/sending/waiting_for_data"]])
+        _connected_sending_transferring_0.setAction(self._connected_sending_transferring_0_exec)
+        _connected_sending_transferring_0.setTrigger(Event("sent_socket", "socket_in"))
+        _connected_sending_transferring_0.setGuard(self._connected_sending_transferring_0_guard)
+        self.states["/connected/sending/transferring"].addTransition(_connected_sending_transferring_0)
+        
+        # transition /connected/queueing/queueing
+        _connected_queueing_queueing_0 = Transition(self, self.states["/connected/queueing/queueing"], [self.states["/connected/queueing/queueing"]])
+        _connected_queueing_queueing_0.setAction(self._connected_queueing_queueing_0_exec)
+        _connected_queueing_queueing_0.setTrigger(Event("HTTP_input", "request_in"))
+        self.states["/connected/queueing/queueing"].addTransition(_connected_queueing_queueing_0)
+        
+        # transition /connected/parsing/wait_for_header
+        _connected_parsing_wait_for_header_0 = Transition(self, self.states["/connected/parsing/wait_for_header"], [self.states["/connected/parsing/wait_for_payload"]])
+        _connected_parsing_wait_for_header_0.setAction(self._connected_parsing_wait_for_header_0_exec)
+        _connected_parsing_wait_for_header_0.setTrigger(None)
+        _connected_parsing_wait_for_header_0.setGuard(self._connected_parsing_wait_for_header_0_guard)
+        self.states["/connected/parsing/wait_for_header"].addTransition(_connected_parsing_wait_for_header_0)
+        
+        # transition /connected/parsing/wait_for_payload
+        _connected_parsing_wait_for_payload_0 = Transition(self, self.states["/connected/parsing/wait_for_payload"], [self.states["/connected/parsing/wait_for_header"]])
+        _connected_parsing_wait_for_payload_0.setAction(self._connected_parsing_wait_for_payload_0_exec)
+        _connected_parsing_wait_for_payload_0.setTrigger(None)
+        _connected_parsing_wait_for_payload_0.setGuard(self._connected_parsing_wait_for_payload_0_guard)
+        self.states["/connected/parsing/wait_for_payload"].addTransition(_connected_parsing_wait_for_payload_0)
+        _connected_parsing_wait_for_payload_1 = Transition(self, self.states["/connected/parsing/wait_for_payload"], [self.states["/connected/parsing/wait_for_header"]])
+        _connected_parsing_wait_for_payload_1.setAction(self._connected_parsing_wait_for_payload_1_exec)
+        _connected_parsing_wait_for_payload_1.setTrigger(None)
+        _connected_parsing_wait_for_payload_1.setGuard(self._connected_parsing_wait_for_payload_1_guard)
+        self.states["/connected/parsing/wait_for_payload"].addTransition(_connected_parsing_wait_for_payload_1)
+        
+        # transition /connecting
+        _connecting_0 = Transition(self, self.states["/connecting"], [self.states["/waiting"]])
+        _connecting_0.setAction(self._connecting_0_exec)
+        _connecting_0.setTrigger(Event("_0after"))
+        self.states["/connecting"].addTransition(_connecting_0)
+    
+    def _connecting_enter(self):
+        self.addTimer(0, self.timeout)
+    
+    def _connecting_exit(self):
+        self.removeTimer(0)
+    
+    def _init_enter(self):
+        self.ID = str(uuid.uuid4())
+        self.big_step.outputEvent(Event("create_socket", "socket_out", [self.ID]))
+    
+    def _waiting_enter(self):
+        self.big_step.outputEvent(Event("http_client_initialized", "request_out", []))
+    
+    def _connecting_connecting_enter(self):
+        self.big_step.outputEvent(Event("connect_socket", "socket_out", [self.socket, self.address]))
+    
+    def _connecting_cooldown_enter(self):
+        self.addTimer(1, 0.1)
+    
+    def _connecting_cooldown_exit(self):
+        self.removeTimer(1)
+    
+    def _connected_listening_listen_enter(self):
+        self.big_step.outputEvent(Event("recv_socket", "socket_out", [self.socket]))
+    
+    def _connected_queueing_queueing_enter(self):
+        pass
+    
+    def _connecting_0_exec(self, parameters):
+        self.big_step.outputEvent(Event("http_client_timeout", "request_out", []))
+    
+    def _init_0_exec(self, parameters):
+        socket = parameters[0]
+        ID = parameters[1]
+        self.socket = socket
+    
+    def _init_0_guard(self, parameters):
+        socket = parameters[0]
+        ID = parameters[1]
+        return self.ID == ID
+    
+    def _waiting_0_exec(self, parameters):
+        address = parameters[0]
+        timeout = parameters[1]
+        self.address = address
+        self.timeout = timeout
+    
+    def _connecting_connecting_1_exec(self, parameters):
+        socket = parameters[0]
+        self.big_step.outputEvent(Event("http_client_ready", "request_out", []))
+    
+    def _connecting_connecting_1_guard(self, parameters):
+        socket = parameters[0]
+        return self.socket == socket
+    
+    def _connected_listening_listen_0_exec(self, parameters):
+        socket = parameters[0]
+        data = parameters[1]
+        self.received_data += data
+    
+    def _connected_listening_listen_0_guard(self, parameters):
+        socket = parameters[0]
+        data = parameters[1]
+        return (self.socket == socket) and (len(data) > 0)
+    
+    def _connected_listening_listen_1_guard(self, parameters):
+        socket = parameters[0]
+        data = parameters[1]
+        return (self.socket == socket) and (len(data) == 0)
+    
+    def _connected_sending_waiting_for_data_0_exec(self, parameters):
+        self.big_step.outputEvent(Event("send_socket", "socket_out", [self.socket, self.send_data]))
+    
+    def _connected_sending_waiting_for_data_0_guard(self, parameters):
+        return len(self.send_data) > 0
+    
+    def _connected_sending_transferring_0_exec(self, parameters):
+        socket = parameters[0]
+        sent_bytes = parameters[1]
+        self.send_data = self.send_data[sent_bytes:]
+    
+    def _connected_sending_transferring_0_guard(self, parameters):
+        socket = parameters[0]
+        sent_bytes = parameters[1]
+        return self.socket == socket
+    
+    def _connected_queueing_queueing_0_exec(self, parameters):
+        data = parameters[0]
+        ID = parameters[1]
+        self.send_data += "POST / HTTP/1.0\r\n"
+        self.send_data += "Content-Length: %i\r\n" % len(str(data))
+        self.send_data += "\r\n"
+        self.send_data += data
+        self.IDs.append(ID)
+    
+    def _connected_parsing_wait_for_header_0_exec(self, parameters):
+        header, self.received_data = self.received_data.split("\r\n\r\n", 1)
+        header = header.lower()
+        if "content-length" in header:
+            _, after = header.split("content-length:", 1)
+            after, _ = after.split("\r\n", 1)
+            after = after.strip()
+            self.length = int(after)
+        else:
+            self.length = float('inf')
+    
+    def _connected_parsing_wait_for_header_0_guard(self, parameters):
+        return '\r\n\r\n' in self.received_data
+    
+    def _connected_parsing_wait_for_payload_0_exec(self, parameters):
+        data = self.received_data[:self.length]
+        self.received_data = self.received_data[self.length:]
+        self.big_step.outputEvent(Event("HTTP_output", "request_out", [data, self.IDs.pop(0)]))
+    
+    def _connected_parsing_wait_for_payload_0_guard(self, parameters):
+        return len(self.received_data) >= self.length and self.IDs[0] is not None
+    
+    def _connected_parsing_wait_for_payload_1_exec(self, parameters):
+        # Drop data
+        self.received_data = self.received_data[self.length:]
+        self.IDs.pop(0)
+    
+    def _connected_parsing_wait_for_payload_1_guard(self, parameters):
+        return len(self.received_data) >= self.length and self.IDs[0] is None
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/init"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class ObjectManager(ObjectManagerBase):
+    def __init__(self, controller):
+        ObjectManagerBase.__init__(self, controller)
+    
+    def instantiate(self, class_name, construct_params):
+        if class_name == "HTTPClient":
+            instance = HTTPClient(self.controller)
+            instance.associations = {}
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(ThreadsControllerBase):
+    def __init__(self, keep_running = None, behind_schedule_callback = None):
+        if keep_running == None: keep_running = True
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.addInputPort("socket_in")
+        self.addInputPort("request_in")
+        self.addOutputPort("socket_out")
+        self.addOutputPort("request_out")
+        self.object_manager.createInstance("HTTPClient", [])

+ 192 - 0
wrappers/http_client.xml

@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<diagram author="Yentl Van Tendeloo" name="HTTP client">
+    <description>
+        HTTP client.
+    </description>
+    <top>
+        import uuid
+    </top>
+
+    <inport name="socket_in"/>
+    <outport name="socket_out"/>
+
+    <inport name="request_in"/>
+    <outport name="request_out"/>
+
+    <class name="HTTPClient">
+        <constructor>
+            <body>
+                <![CDATA[
+                self.socket = None
+                self.received_data = ""
+                self.send_data = ""
+                self.queue = []
+                self.IDs = []
+                ]]>
+            </body>
+        </constructor>
+        <scxml initial="init">
+            <state id="init">
+                <onentry>
+                    <script>
+                        self.ID = str(uuid.uuid4())
+                    </script>
+                    <raise scope="output" event="create_socket" port="socket_out">
+                        <parameter expr="self.ID"/>
+                    </raise>
+                </onentry>
+
+                <transition port="socket_in" event="created_socket" cond="self.ID == ID" target="../waiting">
+                    <parameter name="socket"/>
+                    <parameter name="ID"/>
+                    <script>
+                        self.socket = socket
+                    </script>
+                </transition>
+            </state>
+
+            <state id="waiting">
+                <onentry>
+                    <raise scope="output" port="request_out" event="http_client_initialized"/>
+                </onentry>
+
+                <transition port="request_in" event="connect" target="../connecting">
+                    <parameter name="address"/>
+                    <parameter name="timeout"/>
+
+                    <script>
+                        self.address = address
+                        self.timeout = timeout
+                    </script>
+                </transition>
+            </state>
+
+            <state id="connecting" initial="connecting">
+                <state id="connecting">
+                    <onentry>
+                        <raise scope="output" event="connect_socket" port="socket_out">
+                            <parameter expr="self.socket"/>
+                            <parameter expr="self.address"/>
+                        </raise>
+                    </onentry>
+
+                    <transition port="socket_in" event="error_socket" target="../cooldown"/>
+
+                    <transition port="socket_in" event="connected_socket" cond="self.socket == socket" target="../../connected">
+                        <parameter name="socket"/>
+                        <raise scope="output" port="request_out" event="http_client_ready"/>
+                    </transition>
+                </state>
+
+                <state id="cooldown">
+                    <transition after="0.1" target="../connecting"/>
+                </state>
+
+                <transition after="self.timeout" target="../waiting">
+                    <raise scope="output" port="request_out" event="http_client_timeout"/>
+                </transition>
+            </state>
+
+            <parallel id="connected">
+                <state id="listening" initial="listen">
+                    <state id="listen">
+                        <onentry>
+                            <raise scope="output" port="socket_out" event="recv_socket">
+                                <parameter expr="self.socket"/>
+                            </raise>
+                        </onentry>
+                        <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) > 0)" target=".">
+                            <parameter name="socket"/>
+                            <parameter name="data"/>
+                            <script>
+                                self.received_data += data
+                            </script>
+                        </transition>
+                        <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) == 0)" target="../close">
+                            <parameter name="socket"/>
+                            <parameter name="data"/>
+                        </transition>
+                    </state>
+                    <state id="close">
+                    </state>
+                </state>
+
+                <state id="sending" initial="waiting_for_data">
+                    <state id="waiting_for_data">
+                        <transition cond="len(self.send_data) > 0" target="../transferring">
+                            <raise scope="output" port="socket_out" event="send_socket">
+                                <parameter expr="self.socket"/>
+                                <parameter expr="self.send_data"/>
+                            </raise>
+                        </transition>
+                    </state>
+                    <state id="transferring">
+                        <transition event="sent_socket" port="socket_in" cond="self.socket == socket" target="../waiting_for_data">
+                            <parameter name="socket"/>
+                            <parameter name="sent_bytes"/>
+                            <script>
+                                self.send_data = self.send_data[sent_bytes:]
+                            </script>
+                        </transition>
+                    </state>
+                </state>
+
+                <state id="queueing">
+                    <state id="queueing">
+                        <onentry>
+                        </onentry>
+                        <transition port="request_in" event="HTTP_input" target=".">
+                            <parameter name="data"/>
+                            <parameter name="ID"/>
+                            <script>
+                                self.send_data += "POST / HTTP/1.0\r\n"
+                                self.send_data += "Content-Length: %i\r\n" % len(str(data))
+                                self.send_data += "\r\n"
+                                self.send_data += data
+                                self.IDs.append(ID)
+                            </script>
+                        </transition>
+                    </state>
+                </state>
+
+                <state id="parsing" initial="wait_for_header">
+                    <state id="wait_for_header">
+                        <transition cond="'\r\n\r\n' in self.received_data" target="../wait_for_payload">
+                            <script>
+                                header, self.received_data = self.received_data.split("\r\n\r\n", 1)
+                                header = header.lower()
+                                if "content-length" in header:
+                                    _, after = header.split("content-length:", 1)
+                                    after, _ = after.split("\r\n", 1)
+                                    after = after.strip()
+                                    self.length = int(after)
+                                else:
+                                    self.length = float('inf')
+                            </script>
+                        </transition>
+                    </state>
+                    <state id="wait_for_payload">
+                        <transition cond="len(self.received_data) >= self.length and self.IDs[0] is not None" target="../wait_for_header">
+                            <script>
+                                data = self.received_data[:self.length]
+                                self.received_data = self.received_data[self.length:]
+                            </script>
+                            <raise event="HTTP_output" scope="output" port="request_out">
+                                <parameter expr="data"/>
+                                <parameter expr="self.IDs.pop(0)"/>
+                            </raise>
+                        </transition>
+
+                        <transition cond="len(self.received_data) >= self.length and self.IDs[0] is None" target="../wait_for_header">
+                            <script>
+                                # Drop data
+                                self.received_data = self.received_data[self.length:]
+                                self.IDs.pop(0)
+                            </script>
+                        </transition>
+                    </state>
+                </state>
+            </parallel>
+        </scxml>
+    </class>
+</diagram>

+ 99 - 95
wrappers/modelverse_coded.py

@@ -1,12 +1,10 @@
 import urllib
-import urllib2
 import json
 import random
-from urllib2 import URLError
 import sys
 import time
 import threading
-import Queue
+import uuid
 
 from sccd.runtime.statecharts_core import Event
 
@@ -24,6 +22,8 @@ MODE_SERVICE = 6
 sys.path.append(COMPILER_PATH)
 from hutn_compiler.compiler import main as do_compile
 
+import socket2event
+
 # Exceptions
 class ModelverseException(Exception):
     pass
@@ -63,20 +63,25 @@ class UnknownMetamodellingHierarchy(ModelverseException):
 
 # Helper functions and configuration: do not use yourself!
 taskname = None
-address = None
 mode = MODE_UNCONNECTED
 prev_mode = None
 current_model = None
 registered_metamodels = {}
 outputs = [None]
+ctrl_input = None
+ctrl_output = None
 
 def _output_thread(outputs, taskname):
-    while taskname is None:
-        time.sleep(0.1)
+    req_out = ctrl_output.addOutputListener("request_out")
+    my_id = str(uuid.uuid4())
 
     try:
         while 1:
-            outputs.append(json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname}))).read()))
+            ctrl_output.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "get_output", "taskname": taskname}), my_id]))
+
+            event = req_out.fetch(-1)
+            if event.parameters[1] == my_id:
+                outputs.append(json.loads(event.parameters[0]))
     except:
         pass
 
@@ -103,7 +108,7 @@ def _exec_on_statechart(statechart):
                 # Expand the event and make it HTTP input
                 _input(input_event.parameters)
                 
-            time.sleep(0.01)
+            time.sleep(0.02)
         
     thrd = threading.Thread(target=_exec_sc, args=statechart)
     thrd.daemon = True
@@ -160,20 +165,19 @@ def _goto_mode(new_mode, model_name=None):
 
 def _input(value, port=None):
     # Ugly json encoding of primitives
-    print("[IN] %s" % value)
+    #print("[IN] %s" % value)
     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()
+        ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": port, "data": value}), None]))
     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()
+        ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": port, "value": value}), None]))
 
 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()
+    ctrl_input.addInput(Event("HTTP_input", "request_in", [urllib.urlencode({"op": "set_input", "taskname": taskname, "value": value}), None]))
 
 def _compile_AL(code):
     # Compile an action language file and send the compiled code
@@ -212,11 +216,10 @@ def _compile_model(code):
 def _output(expected=None):
     try:
         while len(outputs) < 2:
-            time.sleep(0.5)
-            print("Sleep for output: " + str(outputs))
+            time.sleep(0.02)
 
         del outputs[0]
-        print("[OUT] %s" % outputs[0])
+        #print("[OUT] %s" % outputs[0])
     except:
         raise UnknownError()
 
@@ -228,7 +231,7 @@ def _last_output():
     return outputs[0]
 
 # Raise common exceptions
-def _handle_output(requested=None, split=None):
+def _handle_output(requested=None, split=False):
     value = _output()
     if value.startswith("Model exists: "):
         raise ModelExists(value.split(": ", 1)[1])
@@ -243,14 +246,14 @@ def _handle_output(requested=None, split=None):
     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:
+        if not split:
             return value
         else:
-            splitted = value.strip().split(split, 1)
-            if len(splitted) == 1:
-                return ""
+            splitted = value.strip().split(" ", 1)
+            if len(splitted) > 1:
+                return splitted[1].split("\n")
             else:
-                return splitted[1].rstrip()
+                return []
     else:
         raise InterfaceMismatch(value)
     
@@ -296,23 +299,52 @@ def alter_context(model_name, metamodel_name):
     registered_metamodels[model_name] = metamodel_name
 
 # Main MvC operations
-def init(address_param="http://127.0.0.1:8001", timeout=20.0):
+def init(address_param="127.0.0.1:8001", timeout=20.0):
     """Starts up the connection to the Modelverse."""
+    # Start up the HTTP Client SC
+    import http_client
+    global ctrl_input
+    global ctrl_output
+
+    if ctrl_input is not None:
+        ctrl_input.stop()
+    if ctrl_output is not None:
+        ctrl_output.stop()
+
+    ctrl_input = http_client.Controller()
+    ctrl_output = http_client.Controller()
+    controllers = [ctrl_input, ctrl_output]
+
+    addr, port = address_param.split(":", 1)
+    port = int(port)
+
+    for ctrl in controllers:
+        listener = ctrl.addOutputListener("request_out")
+        socket2event.boot_translation_service(ctrl)
+        thrd = threading.Thread(target=ctrl.start)
+        thrd.daemon = True
+        thrd.start()
+
+        evt = listener.fetch(-1)
+        if evt.name != "http_client_initialized":
+            raise Exception("HTTP client did not behave as expected during init: " + str(evt.name))
+
+        ctrl.addInput(Event("connect", "request_in", [(addr, port), timeout]))
+
+        evt = listener.fetch(-1)
+
+        if evt.name == "http_client_timeout":
+            raise Exception("HTTP client timeout")
+        if evt.name != "http_client_ready":
+            raise Exception("HTTP client did not behave as expected during connect: " + str(evt.name))
+
     global mode
-    global address
-    address = address_param
     start_time = time.time()
     task = random.random()
-    while 1:
-        try:
-            _input_raw('"%s"' % task, "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)
+
+    _input_raw('"%s"' % task, "task_manager")
+
+    mode = MODE_UNAUTHORIZED
 
     global outputs
     global taskname
@@ -399,19 +431,16 @@ def model_list(location):
     """List all models."""
     _goto_mode(MODE_MODELLING)
     _input(["model_list", location])
-    return set(_handle_output("Success: ", split=" ").split("\n"))
+    return set(_handle_output("Success: ", split=True))
 
 def model_list_full(location):
     """List full information on all models."""
     _goto_mode(MODE_MODELLING)
     _input(["model_list_full", location])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
+    output = _handle_output("Success: ", split=True)
 
     lst = set([])
-    value = output.strip().split("\n")
-    for v in value:
+    for v in output:
         m = v.strip()
         perm, own, grp, m = m.split(" ", 3)
         lst.add((m, own, grp, perm))
@@ -426,7 +455,7 @@ def verify(model_name, metamodel_name=None):
         metamodel_name = _get_metamodel(model_name)
 
     _input(["verify", model_name, metamodel_name])
-    return _handle_output("Success: ", split=" ")
+    return _handle_output("Success: ", split=True)[0]
 
 def model_overwrite(model_name, new_model=None, metamodel_name=None):
     """Upload a new model and overwrite an existing model."""
@@ -468,16 +497,14 @@ def model_render(model_name, mapper_name):
     _goto_mode(MODE_MODELLING)
 
     _input(["model_render", model_name, mapper_name])
-    return json.loads(_handle_output("Success: ", split=" "))
+    return json.loads(_handle_output("Success: ", split=True)[0])
 
 def transformation_between(source, target):
     _goto_mode(MODE_MODELLING)
 
     _input(["transformation_between", source, target])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-    return set([v for v in output.split("\n")])
+    output = _handle_output("Success: ", split=True)
+    return set(output)
 
 def transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None):
     """Create a new model transformation."""
@@ -639,13 +666,10 @@ def transformation_list():
     _goto_mode(MODE_MODELLING)
 
     _input("transformation_list")
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
+    output = _handle_output("Success: ", split=True)
 
     lst = set([])
-    value = output.strip().split("\n")
-    for v in value:
+    for v in output:
         t, m = v.strip().split(" ", 1)
         t = t[1:-1].strip()
         m = m.strip().split(":")[0].strip()
@@ -767,10 +791,8 @@ def element_list(model_name):
 
     _input("list_full")
     lst = set([])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-    for v in output.split("\n"):
+    output = _handle_output("Success: ", split=True)
+    for v in output:
         m, mm = v.split(":")
         m = m.strip()
         mm = mm.strip()
@@ -783,10 +805,8 @@ def types(model_name):
 
     _input("types")
     lst = set([])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-    for v in output.split("\n"):
+    output = _handle_output("Success: ", split=True)
+    for v in output:
         m, mm = v.split(":")
         m = m.strip()
         lst.add(m)
@@ -798,10 +818,8 @@ def types_full(model_name):
 
     _input("types")
     lst = set([])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-    for v in output.split("\n"):
+    output = _handle_output("Success: ", split=True)
+    for v in output:
         m, mm = v.split(":")
         m = m.strip()
         mm = mm.strip()
@@ -813,8 +831,7 @@ def read(model_name, ID):
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read", ID])
-    output = _handle_output("Success: ", split=" ")
-    v = output.split("\n")
+    v = _handle_output("Success: ", split=True)
     t = v[1].split(":")[1].strip()
     if (not v[2].startswith("Source:")):
         rval = (t, None)
@@ -829,11 +846,10 @@ def read_attrs(model_name, ID):
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read", ID])
-    output = _handle_output("Success: ", split=" ")
-    v = output.split("\n")
+    output = _handle_output("Success: ", split=True)
     searching = True
     rval = {}
-    for r in v:
+    for r in output:
         if searching:
             if r == "Attributes:":
                 # Start working on attributes
@@ -862,7 +878,7 @@ def instantiate(model_name, typename, edge=None, ID=""):
         _input(["instantiate_node", typename, ID])
     else:
         _input(["instantiate_edge", typename, ID, edge[0], edge[1]])
-    return _handle_output("Success: ", split=" ")
+    return _handle_output("Success: ", split=True)[0]
 
 def delete_element(model_name, ID):
     """Delete the element with the given ID"""
@@ -906,36 +922,30 @@ def read_outgoing(model_name, ID, typename):
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read_outgoing", ID, typename])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-    else:
-        return set(output.split("\n"))
+    output = _handle_output("Success: ", split=True)
+    return set(output)
 
 def read_incoming(model_name, ID, typename):
     """Returns a list of all incoming associations of a specific type ("" = all)"""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read_incoming", ID, typename])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-    else:
-        return set(output.split("\n"))
+    output = _handle_output("Success: ", split=True)
+    return set(output)
 
 def read_association_source(model_name, ID):
     """Returns the source of an association."""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read_association_source", ID])
-    return _handle_output("Success: ", split=" ")
+    return _handle_output("Success: ", split=True)[0]
 
 def read_association_destination(model_name, ID):
     """Returns the destination of an association."""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["read_association_destination", ID])
-    return _handle_output("Success: ", split=" ")
+    return _handle_output("Success: ", split=True)[0]
 
 ##### To document:
 
@@ -955,7 +965,7 @@ def service_register(name, function):
 
     # Now we are in service-mode
     mode = MODE_SERVICE
-    port = _handle_output("Success: ", split=" ")
+    port = _handle_output("Success: ", split=True)[0]
 
     # Process events in the background!
     threading.Thread(target=service_process, args=[port]).start()
@@ -996,7 +1006,7 @@ def element_list_nice(model_name):
 
     _input(["element_list_nice", model_name, _get_metamodel(model_name)])
 
-    data = _handle_output("Success: ", split=" ")
+    data = _handle_output("Success: ", split=True)[0]
     try:
         return json.loads(data)
     except:
@@ -1008,29 +1018,23 @@ def connections_between(model_name, source_element, target_element):
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["connections_between", source_element, target_element])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-    else:
-        return set(output.split("\n"))
+    output = _handle_output("Success: ", split=True)
+    return set(output)
 
 def define_attribute(model_name, node, attr_name, attr_type):
     """Create a new attribute, which can be instantiated one meta-level below."""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["define_attribute", node, attr_name, attr_type])
-    return _handle_output("Success: ", split=" ")
+    return _handle_output("Success: ", split=True)[0]
 
 def all_instances(model_name, type_name):
     """Returns a list of all elements of a specific type."""
     _goto_mode(MODE_MODIFY, model_name)
 
     _input(["all_instances", type_name])
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-    else:
-        return set(output.split("\n"))
+    output = _handle_output("Success: ", split=True)
+    return set(output)
 
 def service_poll(port):
     """Checks whether or not the Modelverse side has any input ready to be processed."""

+ 118 - 0
wrappers/socket2event.py

@@ -0,0 +1,118 @@
+import threading
+from sccd.runtime.statecharts_core import Event
+import socket
+
+send_data_queues = {}
+send_events = {}
+recv_events = {}
+run_sockets = {}
+
+def start_socket_threads(controller, sock):
+    recv_events[sock] = recv_event = threading.Event()
+    send_events[sock] = send_event = threading.Event()
+    send_data_queues[sock] = send_data_queue = []
+    run_sockets[sock] = True
+
+    thrd = threading.Thread(target=receive_from_socket, args=[controller, sock, recv_event])
+    thrd.daemon = True
+    thrd.start()
+
+    thrd = threading.Thread(target=send_to_socket, args=[controller, sock, send_data_queue, send_event])
+    thrd.daemon = True
+    thrd.start()
+
+def receive_from_socket(controller, sock, recv_event):
+    while 1:
+        recv_event.wait()
+        recv_event.clear()
+        if not run_sockets[sock]:
+            break
+        data = sock.recv(2**16)
+        controller.addInput(Event("received_socket", "socket_in", [sock, data]))
+
+def send_to_socket(controller, sock, data_queue, send_event):
+    while run_sockets[sock]:
+        send_event.wait()
+        send_event.clear()
+        while data_queue:
+            send = sock.send(data_queue.pop(0))
+            controller.addInput(Event("sent_socket", "socket_in", [sock, send]))
+        if not run_sockets[sock]:
+            break
+
+def _accept(controller, sock):
+    conn, addr = sock.accept()
+    start_socket_threads(controller, conn)
+    controller.addInput(Event("accepted_socket", "socket_in", [sock, conn]))
+
+def _connect(controller, sock, destination):
+    sock.connect(destination)
+    controller.addInput(Event("connected_socket", "socket_in", [sock]))
+
+def _close(controller, sock):
+    run_sockets[sock] = False
+    send_events[sock].set()
+    recv_events[sock].set()
+    sock.close()
+    controller.addInput(Event("closed_socket", "socket_in", [sock]))
+
+def _bind(controller, sock, addr):
+    sock.bind(addr)
+    controller.addInput(Event("bound_socket", "socket_in", [sock]))
+
+def _listen(controller, sock):
+    sock.listen(1)
+    controller.addInput(Event("listened_socket", "socket_in", [sock]))
+
+def _wrapper_func(*args):
+    func = args[0]
+    controller = args[1]
+    sock = args[2]
+    try:
+        func(*args[1:])
+    except socket.error as e:
+        print("ERROR " + str(e))
+        controller.addInput(Event("error_socket", "socket_in", [sock, e]))
+    except Exception as e:
+        print("UNKNOWN ERROR " + str(e))
+        controller.addInput(Event("unknown_error_socket", "socket_in", [sock, e]))
+        raise
+
+def _start_on_daemon_thread(func, args):
+    new_args = [func]
+    new_args.extend(args)
+    args = new_args
+    thrd = threading.Thread(target=_wrapper_func, args=args)
+    thrd.daemon = True
+    thrd.start()
+
+def boot_translation_service(controller):
+    socket_out = controller.addOutputListener("socket_out")
+    _start_on_daemon_thread(_poll, [controller, socket_out])
+
+def _poll(controller, socket_out):
+    while 1:
+        evt = socket_out.fetch(-1)
+        name, params = evt.getName(), evt.getParameters()
+        if name == "accept_socket":
+            _start_on_daemon_thread(_accept, [controller, params[0]])
+        elif name == "recv_socket":
+            recv_events[params[0]].set()
+        elif name == "connect_socket":
+            _start_on_daemon_thread(_connect, [controller, params[0], params[1]])
+        elif name == "create_socket":
+            sock = socket.socket()
+            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            start_socket_threads(controller, sock)
+            controller.addInput(Event("created_socket", "socket_in", [sock, params[0]]))
+        elif name == "close_socket":
+            _start_on_daemon_thread(_close, [controller, params[0]])
+        elif name == "send_socket":
+            send_data_queues[params[0]].append(params[1])
+            send_events[params[0]].set()
+        elif name == "bind_socket":
+            _start_on_daemon_thread(_bind, [controller, params[0], params[1]])
+        elif name == "listen_socket":
+            _start_on_daemon_thread(_listen, [controller, params[0]])
+        elif name == "stop":
+            break

+ 2 - 0
wrappers/test_SCCD.py

@@ -25,6 +25,7 @@ model_add("models/my_SCCD", "formalisms/SCCD", open("models/dynamic_trafficlight
 # Add SCCD execution semantics
 transformation_add_AL({"SCCD": "formalisms/SCCD"}, {"trace": "formalisms/SCCD_Trace"}, "models/SCCD_execute", open("models/SCCD_execute.alc", 'r').read())
 
+"""
 import poll_print
 ctrl = poll_print.Controller(keep_running=False)
 
@@ -40,3 +41,4 @@ while thrd.is_alive():
         ctrl.addInput(Event("raw_inp", "user_inp", [sys.stdin.readline().strip()]))
 
 thrd.join()
+"""