<class name="Task">
    <relationships>
        <association name="parent" class="MvKController" min="1" max="1"/>
    </relationships>
    <constructor>
        <parameter name="taskname"/>
        <parameter name="mvs_operations"/>
        <parameter name="mvk"/>
        <body>
            <![CDATA[
            self.taskname = taskname
            self.mvs_operations = mvs_operations
            self.mvk = mvk
            self.unlocked = True

            self.input_queue = []
            self.output_queue = []
            self.outputs = []
            ]]>
        </body>

    </constructor>

    <method name="execute_modelverse">
        <parameter name="taskname"/>
        <parameter name="operation"/>
        <parameter name="params"/>
        <body>
            <![CDATA[
            reply = None
            commands = []
            mvk = self.mvk
            mvs_operations = self.mvs_operations
            try:
                while 1:
                    commands = mvk.execute_yields(taskname, operation, params, reply)
                    if commands is None:
                        break
                    reply = [mvs_operations[command[0]](*(command[1])) for command in commands]
                return (0.0, False)
            except SleepKernel as e:
                return (e.timeout, e.interruptable)
            except KeyboardInterrupt:
                raise
            except:
                import traceback
                print("Error on requests: " + str(commands))
                print("For taskname " + str(taskname))
                stack = [gen for gen in mvk.request_handlers[taskname][operation].generator_stack if gen is not None and gen.__name__ not in ['execute_rule', 'execute_jit']]
                printed_stack = []
                for gen in stack:
                    try:
                        line = gen.gi_frame.f_lineno
                    except:
                        line = "?"
                    try:
                        variables = gen.gi_frame.f_locals
                    except:
                        variables = "?"
                    printed_stack.append("%s:%s {%s}" % (gen.__name__, line, variables))
                print("Stack @ MvK:\n" + str("\n".join(printed_stack)))
                print(traceback.format_exc())
                return (float('inf'), False)
            ]]>
        </body>
    </method>

    <scxml initial="main">
        <parallel id="main">
            <state id="queue">
                <state id="queue">
                    <transition event="input" target=".">
                        <parameter name="params"/>
                        <script>
                            self.input_queue.extend(params)
                        </script>
                    </transition>

                    <transition event="output" target=".">
                        <parameter name="params"/>
                        <script>
                            self.output_queue.append(params)
                        </script>
                    </transition>

                    <transition cond="self.outputs" target=".">
                        <script>
                            source, value = self.outputs.pop(0)
                        </script>
                        <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/%s' % source">
                            <parameter expr="json.dumps(value)"/>
                        </raise>
                    </transition>
                </state>
            </state>

            <state id="process" initial="running">
                <state id="running" initial="components">
                    <transition event="suspend" target="../suspended"/>

                    <history id="history" type="deep"/>

                    <parallel id="components">
                        <state id="input">
                            <state id="input">
                                <transition cond="self.input_queue" target=".">
                                    <script>
                                        for args_entry in self.input_queue:
                                            self.execute_modelverse(self.taskname, "set_input", [args_entry])
                                        self.input_queue = []
                                    </script>
                                    <raise event="wake_timer"/>
                                </transition>
                            </state>
                        </state>

                        <state id="processing" initial="processing">
                            <state id="processing">
                                <onentry>
                                    <script>
                                        start_time = time.time()
                                        # Grant each task some milliseconds of execution
                                        while (time.time() - start_time &lt; 0.05):
                                            timeout = self.execute_modelverse(self.taskname, "execute_rule", [])
                                            if timeout[0] > 0.0:
                                                # We should not continue immediately
                                                break
                                        self.timeout = timeout
                                    </script>
                                </onentry>

                                <transition cond="self.timeout[0] == 0.0" after="self.sccd_yield()" target="."/>
                                <transition cond="0.0 &lt; self.timeout[0] &lt; float('inf')" after="self.sccd_yield()" target="../blocked">
                                    <script>
                                        self.unlocked = False
                                    </script>
                                    <raise event="start_timer">
                                        <parameter expr="self.timeout[0]"/>
                                        <parameter expr="self.timeout[1]"/>
                                    </raise>
                                </transition>
                                <transition cond="self.timeout[0] == float('inf')" target="../failed"/>
                            </state>

                            <state id="blocked">
                                <transition cond="self.unlocked" target="../processing"/>
                            </state>

                            <state id="failed">
                                <script>
                                    print("TODO: task has failed")
                                </script>
                            </state>
                        </state>

                        <state id="output">
                            <state id="output">
                                <onentry>
                                    <script>
                                        if self.output_queue:
                                            if self.execute_modelverse(self.taskname, "get_output", []):
                                                if self.mvk.success:
                                                    self.outputs.append((self.output_queue.pop(0), self.mvk.returnvalue))
                                    </script>
                                </onentry>

                                <transition after="self.sccd_yield() + 0.1" target="."/>
                            </state>
                        </state>
                    </parallel>
                </state>

                <state id="suspended">
                    <transition event="resume" target="../running/history"/>
                </state>
            </state>

            <state id="timer" initial="ready">
                <state id="waiting">
                    <transition after="self.sccd_yield() + self.timer_duration" target="../ready"/>
                    <transition event="wake_timer" cond="self.interruptable" target="../ready"/>
                </state>

                <state id="ready">
                    <onentry>
                        <script>
                            self.unlocked = True
                        </script>
                    </onentry>
                    <transition event="start_timer" target="../waiting">
                        <parameter name="duration"/>
                        <parameter name="interruptable"/>
                        <script>
                            self.timer_duration = duration
                            self.interruptable = interruptable
                        </script>
                    </transition>
                </state>
            </state>
        </parallel>
    </scxml>
</class>
