Ver código fonte

Merge branch 'master' of yentl/SCCD-socket2event into master

simon 9 anos atrás
pai
commit
f9b4e6bccb

+ 1 - 1
.gitignore

@@ -10,4 +10,4 @@ csharp_tests/bin
 src/build
 **/*target*
 **/*.lprof
-doc/_build
+doc/_build

+ 2 - 1
doc/index.rst

@@ -24,10 +24,11 @@ Contents
     Runtime Platforms <runtime_platforms>
     Examples <examples>
     Semantic Options <semantic_options>
+    Socket Communication <sockets>
     Internal Documentation <internal_documentation>
     
 References
 ----------
     
 .. [SCCD] Simon Van Mierlo, Yentl Van Tendeloo, Bart Meyers, Joeri Exelmans, and Hans Vangheluwe. SCCD: SCXML extended with class diagrams. In *3rd Workshop on Engineering Interactive Systems with SCXML, part of EICS 2016*, 2016. [`LINK <http://www.scxmlworkshop.de/eics2016/submissions/SCCD%20SCXML%20Extended%20with%20Class%20Diagrams.pdf>`_]
-.. [Statecharts] David Harel. Statecharts: A visual formalism for complex systems. *Sci. Comput. Program. 8*, 3 (1987), 231–274. [`LINK <http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/resources/statecharts.pdf>`_]
+.. [Statecharts] David Harel. Statecharts: A visual formalism for complex systems. *Sci. Comput. Program. 8*, 3 (1987), 231–274. [`LINK <http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/resources/statecharts.pdf>`_]

+ 101 - 0
doc/sockets.rst

@@ -0,0 +1,101 @@
+Socket Communication
+====================
+
+Sockets, for network communication, are an oftenly needed capability for many complex system.
+By default, however, Statecharts, and SCCD in particular, do not handle socket communication at all.
+Shifting all socket communication into action code is not an option either, as it is potentially a blocking operation.
+Additionally, the code wraps different socket implementations and socket configuration.
+
+This module will, after starting the translation service, translate all events on the *socket_in* and *socket_out* port to socket operations.
+Blocking then happens on another thread, while the statechart can continue its usual execution.
+When the socket operation returns, the result will be raised in the statechart as an event.
+
+Initialization
+--------------
+
+To use the translation service, several steps should be followed:
+
+    1. Import sccd.sccd_runtime.socket2event as socket2event;
+    2. Write your model with a *socket_in* and *socket_out* port;
+    3. Before starting the controller, invoke *socket2event.boot_translation_service(controller)* with the controller as its first argument;
+    4. Now raise and catch events as specified here, to communicate with sockets.
+
+Input Events
+------------
+
++-------------------+-----------------------------------+-------------------------------+
+| Event             | Parameters                        | Meaning                       |
++===================+===================================+===============================+
+| accept_socket     | socket                            | socket.accept()               |
++-------------------+-----------------------------------+-------------------------------+
+| recv_socket       | socket                            | socket.recv(2**16)            |
++-------------------+-----------------------------------+-------------------------------+
+| connect_socket    | socket, address                   | socket.connect(address)       |
++-------------------+-----------------------------------+-------------------------------+
+| create_socket     |                                   | new Socket()                  |
++-------------------+-----------------------------------+-------------------------------+
+| close_socket      | socket                            | socket.close()                |
++-------------------+-----------------------------------+-------------------------------+
+| send_socket       | socket, data                      | socket.send(data)             |
++-------------------+-----------------------------------+-------------------------------+
+| bind_socket       | socket, address                   | socket.bind(address)          |
++-------------------+-----------------------------------+-------------------------------+
+| listen_socket     | socket                            | socket.listen()               |
++-------------------+-----------------------------------+-------------------------------+
+| stop              | socket                            | stops translator service      |
++-------------------+-----------------------------------+-------------------------------+
+
+Output Events
+-------------
+
++-----------------------+-----------------------------------+-------------------------------+
+| Event                 | Arguments                         | Response to                   |
++=======================+===================================+===============================+
+| received_socket       | socket, data                      | recv_socket                   |
++-----------------------+-----------------------------------+-------------------------------+
+| sent_socket           | socket, bytes                     | send_socket                   |
++-----------------------+-----------------------------------+-------------------------------+
+| accepted_socket       | socket, connection                | accept_socket                 |
++-----------------------+-----------------------------------+-------------------------------+
+| connected_socket      | socket                            | connect_socket                |
++-----------------------+-----------------------------------+-------------------------------+
+| closed_socket         | socket                            | close_socket                  |
++-----------------------+-----------------------------------+-------------------------------+
+| bound_socket          | socket                            | bind_socket                   |
++-----------------------+-----------------------------------+-------------------------------+
+| listened_socket       | socket                            | listen_socket                 |
++-----------------------+-----------------------------------+-------------------------------+
+| error_socket          | socket, error                     | Socket error occurs           |
++-----------------------+-----------------------------------+-------------------------------+
+| unknown_error_socket  | socket, error                     | Python error occurs           |
++-----------------------+-----------------------------------+-------------------------------+
+
+HTTP client/server
+------------------
+
+Using this library, an HTTP echo client and server are implemented.
+The server echoes all data received from the client.
+The client connects to the server and sends some data.
+These are included in the examples directory
+
+Compile the server using::
+
+   python python_sccd_compiler/sccdc.py -p threads server.xml
+
+and the client using::
+
+   python python_sccd_compiler/sccdc.py -p threads client.xml
+
+Afterwards, you can run the server as::
+
+   python run_server.py
+
+which will start up a simple HTTP echo server on port 8080
+(configurable in constructor).
+Then you can start up several clients using::
+
+   python run_client.py
+
+The client will send out a counter to the server and print out the
+reply. The server is able to connect to multiple clients simultaneously,
+so can handle multiple open connections without getting confused.

+ 2 - 2
examples/HTTP_client/run_client.py

@@ -1,8 +1,8 @@
 import sys
 
 import client
-import socket2event
+from sccd.runtime import socket2event
 
 controller = client.Controller(sys.argv[1:])
 socket2event.boot_translation_service(controller)
-controller.start()
+controller.start()

+ 2 - 2
examples/HTTP_server/run_echo_server.py

@@ -1,8 +1,8 @@
 import sys
 
 import server
-import socket2event
+from sccd.runtime import socket2event
 
 controller = server.Controller(sys.argv[1:])
 socket2event.boot_translation_service(controller)
-controller.start()
+controller.start()

+ 0 - 117
examples/HTTP_server/socket2event.py

@@ -1,117 +0,0 @@
-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]))
-
-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]))
-        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

+ 16 - 4
examples/HTTP_client/socket2event.py

@@ -1,5 +1,17 @@
-import threading
+"""
+Socket 2 Event wrapper
+
+Author: Yentl Van Tendeloo
+
+This maps socket communication to events, and vice versa, allowing for a
+Statechart to use (blocking) sockets. It sends events to the socket_in port,
+and listens for commands on the socket_out port.  As this runs on its own
+thread, you will need to start the code by running
+"boot_translation_service(controller)" before using the ports.
+"""
+
 from sccd.runtime.statecharts_core import Event
+import threading
 import socket
 
 send_data_queues = {}
@@ -86,10 +98,10 @@ def _start_on_daemon_thread(func, args):
     thrd.start()
 
 def boot_translation_service(controller):
-    _start_on_daemon_thread(_poll, [controller, None])
-
-def _poll(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()