|
@@ -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", [])
|