浏览代码

First working version of HTTP SCCD model

Yentl Van Tendeloo 8 年之前
父节点
当前提交
6b207ff19f
共有 4 个文件被更改,包括 705 次插入21 次删除
  1. 344 0
      wrappers/http_client.py
  2. 187 0
      wrappers/http_client.xml
  3. 56 21
      wrappers/modelverse.py
  4. 118 0
      wrappers/socket2event.py

+ 344 - 0
wrappers/http_client.py

@@ -0,0 +1,344 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Date:   Thu Aug 24 10:10:13 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 /connected
+        self.states["/connected"] = ParallelState(4, "/connected", self)
+        
+        # state /connected/listening
+        self.states["/connected/listening"] = State(5, "/connected/listening", self)
+        
+        # state /connected/listening/listen
+        self.states["/connected/listening/listen"] = State(6, "/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(7, "/connected/listening/close", self)
+        
+        # state /connected/sending
+        self.states["/connected/sending"] = State(8, "/connected/sending", self)
+        
+        # state /connected/sending/waiting_for_data
+        self.states["/connected/sending/waiting_for_data"] = State(9, "/connected/sending/waiting_for_data", self)
+        
+        # state /connected/sending/transferring
+        self.states["/connected/sending/transferring"] = State(10, "/connected/sending/transferring", self)
+        
+        # state /connected/queueing
+        self.states["/connected/queueing"] = State(11, "/connected/queueing", self)
+        
+        # state /connected/queueing/queueing
+        self.states["/connected/queueing/queueing"] = State(12, "/connected/queueing/queueing", self)
+        self.states["/connected/queueing/queueing"].setEnter(self._connected_queueing_queueing_enter)
+        
+        # state /connected/parsing
+        self.states["/connected/parsing"] = State(13, "/connected/parsing", self)
+        
+        # state /connected/parsing/wait_for_header
+        self.states["/connected/parsing/wait_for_header"] = State(14, "/connected/parsing/wait_for_header", self)
+        
+        # state /connected/parsing/wait_for_payload
+        self.states["/connected/parsing/wait_for_payload"] = State(15, "/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["/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["/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_0 = Transition(self, self.states["/connecting"], [self.states["/connected"]])
+        _connecting_0.setAction(self._connecting_0_exec)
+        _connecting_0.setTrigger(Event("connected_socket", "socket_in"))
+        _connecting_0.setGuard(self._connecting_0_guard)
+        self.states["/connecting"].addTransition(_connecting_0)
+        _connecting_1 = Transition(self, self.states["/connecting"], [self.states["/waiting"]])
+        _connecting_1.setAction(self._connecting_1_exec)
+        _connecting_1.setTrigger(Event("_0after"))
+        self.states["/connecting"].addTransition(_connecting_1)
+        
+        # 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)
+    
+    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_enter(self):
+        self.addTimer(0, self.timeout)
+        self.big_step.outputEvent(Event("connect_socket", "socket_out", [self.socket, self.address]))
+    
+    def _connecting_exit(self):
+        self.removeTimer(0)
+    
+    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 _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
+        print("Connecting")
+    
+    def _connecting_0_exec(self, parameters):
+        socket = parameters[0]
+        self.big_step.outputEvent(Event("http_client_ready", "request_out", []))
+    
+    def _connecting_0_guard(self, parameters):
+        socket = parameters[0]
+        return self.socket == socket
+    
+    def _connecting_1_exec(self, parameters):
+        self.big_step.outputEvent(Event("http_client_timeout", "request_out", []))
+    
+    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:]
+        print("Send out data with ID: " + str(data))
+        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):
+        print("Ignore data: " + str(self.received_data[:self.length]))
+        # 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", [])

+ 187 - 0
wrappers/http_client.xml

@@ -0,0 +1,187 @@
+<?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
+                        print("Connecting")
+                    </script>
+                </transition>
+            </state>
+
+            <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="connected_socket" cond="self.socket == socket" target="../connected">
+                    <parameter name="socket"/>
+                    <raise scope="output" port="request_out" event="http_client_ready"/>
+                </transition>
+
+                <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:]
+                                print("Send out data with ID: " + str(data))
+                            </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>
+                                print("Ignore data: " + str(self.received_data[:self.length]))
+                                # Drop data
+                                self.received_data = self.received_data[self.length:]
+                                self.IDs.pop(0)
+                            </script>
+                        </transition>
+                    </state>
+                </state>
+            </parallel>
+        </scxml>
+    </class>
+</diagram>

+ 56 - 21
wrappers/modelverse.py

@@ -7,6 +7,7 @@ import sys
 import time
 import threading
 import Queue
+import uuid
 
 from sccd.runtime.statecharts_core import Event
 
@@ -24,6 +25,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 +66,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
 
@@ -165,15 +173,17 @@ def _input(value, port=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()
+        #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()
+        #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()
+    #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
@@ -296,23 +306,46 @@ 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
+    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:
+        socket2event.boot_translation_service(ctrl)
+        listener = ctrl.addOutputListener("request_out")
+        thrd = threading.Thread(target=ctrl.start)
+        thrd.daemon = True
+        thrd.start()
+
+        print("Waiting for init")
+        evt = listener.fetch(-1)
+        print("Got init!")
+        if evt.name != "http_client_initialized":
+            raise Exception("HTTP client did not behave as expected during init: " + str(evt.name))
+
+        print("Add input")
+        ctrl.addInput(Event("connect", "request_in", [(addr, port), timeout]))
+
+        evt = listener.fetch(-1)
+        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
@@ -330,7 +363,9 @@ def login(username, password):
     global mode
     _goto_mode(MODE_UNAUTHORIZED)
 
+    print("Wait for output")
     _output("Log on as which user?")
+    print("Got that output")
     _input(username)
     if _output() == "Password for existing user?":
         _input(password)

+ 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):
+    _start_on_daemon_thread(_poll, [controller, None])
+
+def _poll(controller, _):
+    socket_out = controller.addOutputListener("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