<?xml version="1.0" ?>
<diagram author="Simon Van Mierlo/Yentl Van Tendeloo" name="Dynamic Structure DEVS simulator">
    <description>
        A restricted PythonPDEVS simulator modelled in SCCD for classic, dynamic structure DEVS models.
    </description>
    <top>
        import cPickle as pickle
        import time
        import copy

        # We basically just interface with the basesimulator
        from scheduler import Scheduler
        from DEVS import directConnect, CoupledDEVS, AtomicDEVS, RootDEVS
        from classicDEVSWrapper import ClassicDEVSWrapper
        from tracer import trace

        class Breakpoint(object):
            def __init__(self, breakpoint_id, function, enabled, disable_on_trigger):
                self.id = breakpoint_id
                self.function = function
                self.enabled = enabled
                self.disable_on_trigger = disable_on_trigger
    </top>
    <inport name="request"/>
    <outport name="reply"/>
    <class name="SCCDSimulator" default="true">
        <!-- Define the constructor, taking the model to simulate as a parameter -->
        <constructor>
            <parameter name="model"/>
            <body>
                <![CDATA[
                self.model = model
                self.save_model = pickle.dumps(self.model, pickle.HIGHEST_PROTOCOL)
                self.listeners_by_string = {}
                self.initialize_simulation()
                ]]>
            </body>
        </constructor>
        <method name="initialize_simulation">
            <body>
                <![CDATA[
                # Simulation variables
                self.termination_time = None
                self.termination_condition = None
                self.simulation_time = (0.0, 0)
                self.listeners = {}
                self.root_model = self.model
                self.realtime_scale = 1.0
                # Values to be set during simulation
                self.realtime_starttime = None
                self.inject_queue = []
                # Model initialization
                self.model_ids = []
                self.model.finalize(name="", model_counter=0, model_ids=self.model_ids, locations={None: []}, select_hierarchy=[])
                ]]>
            </body>
        </method>
        <!-- Serialize the output values in something human-readable instead of internal objects.
             Note that this doesn't alter the internal structure, as that is still used for simulation. -->
        <method name="serialize">
            <parameter name="type"/>
            <parameter name="object"/>
            <body>
                <![CDATA[
                if type == "imminents":
                    return [m.getModelFullName() for m in object]
                elif type == "imminent":
                    return object.getModelFullName()
                elif type == "outbag":
                    return {m.getPortFullName(): [object[m]] for m in object}
                elif type == "inbags":
                    return {m.getPortFullName(): ([object[m][0].getPortFullName(), object[m][1]] if (object[m][0] is not None) else [object[m][1]]) for m in object}
                elif type == "new_tn" or type == "new_states":
                    return {m.getModelFullName(): object[m] for m in object if m.model_id is not None}
                elif type == "transitioning":
                    return {m.getModelFullName(): {1: "INTERNAL", 2: "EXTERNAL"}[object[m]] for m in object if m.model_id is not None}
                elif type == "transitioned":
                    return {m: {1: "INTERNAL", 2: "EXTERNAL"}[object[m]] for m in object if m.model_id is not None}
                ]]>
            </body>
        </method>
        <!-- Find a port based on a fully qualified name. -->
        <method name="find_port_with_name">
            <parameter name="name"/>
            <body>
                <![CDATA[
                for model in self.model.component_set:
                    if name.startswith(model.getModelFullName()):
                        # Found a potential model
                        # We can't simply check for equality, as portnames might contain dots too
                        for port in model.IPorts:
                            if port.getPortFullName() == name:
                                # Now everything matches
                                return port
                # Nothing found
                return None
                ]]>
            </body>
        </method>
        <!-- Find a model based on a fully qualified name. -->
        <method name="find_model_with_name">
            <parameter name="name"/>
            <body>
                <![CDATA[
                for model in self.model.component_set:
                    if name == model.getModelFullName():
                        # Found exact match
                        return model
                return None
                ]]>
            </body>
        </method>
        <!-- Calculate the time to wait before triggering the next transition.
             This method is also called in non-realtime simulation, so make sure that it returns infinity in that case. -->
        <method name="calculate_after">
            <body>
                <![CDATA[
                try:
                    # Process in parts of 100 milliseconds to repeatedly check the termination condition
                    if self.interrupt_string:
                        nexttime = 0.0
                    else:
                        nexttime = (self.time_next[0] - (time.time() - self.realtime_starttime) / self.realtime_scale) * self.realtime_scale
                    x =  min(0.1, nexttime)
                    return x
                except TypeError, AttributeError:
                    # We are probably not simulating in realtime...
                    return float('inf')
                ]]>
            </body>
        </method>
        <!-- Parse a list of options that can be passed together with the request -->
        <method name="parse_options">
            <parameter name="configuration"/>
            <body>
                <![CDATA[
                self.termination_condition = None if "termination_condition" not in configuration else configuration["termination_condition"]
                self.termination_time = None if "termination_time" not in configuration else configuration["termination_time"]
                self.realtime_scale = 1.0 if "realtime_scale" not in configuration else 1.0/configuration["realtime_scale"]
                # Subtract the current simulation time to allow for pausing
                self.realtime_starttime = (time.time() - self.simulation_time[0]*self.realtime_scale)
                # Reset the time used in the waiting, as it might not get recomputed
                self.the_time = 0.00001
                ]]>
            </body>
        </method>
        <!-- Utility function to determine whether or not simulation is finished. -->
        <method name="should_terminate">
            <parameter name="realtime"/>
            <body>
                <![CDATA[
                # Now that it includes breakpoints, results are to be interpretted as follows:
                # -2 --> continue simulation
                # -1 --> termination condition
                # else --> breakpoint
                if realtime:
                    check_time = self.simulation_time
                else:
                    self.compute_timeNext()
                    check_time = self.time_next
                # Just access the 'transitioned' dictionary
                # Kind of dirty though...
                if self.transitioning is None:
                    transitioned = set()
                else:
                    transitioned = self.transitioning
                outbag = self.outbag if self.outbag is not None else {}
                inbags = self.inbags if self.inbags is not None else {}
                if check_time[0] == float('inf'):
                    # Always terminate if we have reached infinity
                    terminate = True
                elif self.termination_condition is not None:
                    terminate = self.termination_condition(check_time, self.root_model, transitioned)
                else:
                    terminate = self.termination_time < check_time[0]
                if terminate:
                    # Always terminate, so don't check breakpoints
                    return -1
                else:
                    # Have to check breakpoints for termination
                    for bp in self.breakpoints:
                        if not bp.enabled:
                            continue
                        # Include the function in the scope
                        exec(bp.function)
                        # And execute it, note that the breakpoint thus has to start with "def breakpoint("
                        # TODO: report to the user if a breakpoint is invalid (catching exceptions).
                        if breakpoint(check_time, self.root_model, self.serialize('transitioned', transitioned), outbag, inbags):
                            # Triggered!
                            return bp.id
                        else:
                            # Not triggered, so continue
                            continue
                    return -2
                ]]>
            </body>
        </method>
        <!-- Compute all models of which the internal transition function has to be triggered. -->
        <method name="find_internal_imminents">
            <body>
                <![CDATA[
                self.imminents = self.model.scheduler.getImminent(self.simulation_time)
                self.reschedule = set(self.imminents)
                self.transition_times.append(self.simulation_time)
                for model in self.imminents:
                    model.time_next = (model.time_next[0], model.time_next[1] + 1)
                ]]>
            </body>
        </method>
        <!-- Select one of the imminent components -->
        <method name="select_imminent">
            <body>
                <![CDATA[
                self.imminent = None
                if len(self.imminents) > 1:
                    # Perform all selects
                    self.imminents.sort()
                    pending = self.imminents
                    level = 1
                    while len(pending) > 1:
                        # Take the model each time, as we need to make sure that the selectHierarchy is valid everywhere
                        model = pending[0]
                        # Make a set first to remove duplicates
                        colliding = list(set([m.select_hierarchy[level] for m in pending]))
                        chosen = model.select_hierarchy[level-1].select(
                                sorted(colliding, key=lambda i:i.getModelFullName()))
                        pending = [m for m in pending 
                                     if m.select_hierarchy[level] == chosen]
                        level += 1
                    self.imminent = pending[0]
                else:
                    self.imminent = self.imminents[0]
                self.imminent.time_next = (self.imminent.time_next[0], self.imminent.time_next[1] - 1)
                ]]>
            </body>
        </method>
        <!-- Trigger all output functions of imminent models. -->
        <method name="compute_outputfunction">
            <body>
                <![CDATA[
                self.outbag = ClassicDEVSWrapper(self.imminent).outputFnc()
                ]]>
            </body>
        </method>
        <!-- Route all messages and apply the required transfer functions. -->
        <method name="route_messages">
            <body>
                <![CDATA[
                self.inbags = {}
                for outport in self.outbag:
                    payload = self.outbag[outport]
                    for inport, z in outport.routing_outline:
                        if z is not None:
                            payload = [z(pickle.loads(pickle.dumps(m))) for m in payload]
                        self.inbags[inport] = [outport, payload]
                ]]>
            </body>
        </method>
        <!-- Process the injects on the inject_queue.
             Also process realtime interrupts.
             TODO: What about events injected on a port which already has an event, or on a time at which a transition was scheduled? Should this be seen as a separate component that needs priority?
             TODO: Should rejected injects be communicated to the user? -->
        <method name="process_injects">
            <body>
                <![CDATA[
                while self.inject_queue:
                    if self.inject_queue[0]["time"] > self.simulation_time:
                        break
                    config = self.inject_queue.pop(0)
                    portname = config["port"]
                    event = config["event"]
                    port = self.find_port_with_name(portname)
                    if port == None:
                        break
                    self.inbags[port] = [None, [event]]
                if self.interrupt_string:
                    portname, event_value = self.interrupt_string.split(" ")
                    for inport, z in self.root_model.ports[portname].routing_outline:
                        ev = event_value if z is None else z(event_value)
                        self.inbags[inport] = [self.root_model.ports[portname], [ev]]
                    self.interrupt_string = None
                ]]>
            </body>
        </method>
        <!-- Combine the information from the routed messages to determine the external and internal transition functions to trigger. -->
        <method name="find_all_imminents">
            <body>
                <![CDATA[
                # Internal codes:
                #   1 --> internal transition
                #   2 --> external transition
                #   3 --> confluent transition
                # These codes are a legacy of efficient PyPDEVS, but is kept here for consistency
                self.transitioning = {self.imminent: 1}
                for inport in self.inbags:
                    aDEVS = inport.host_DEVS
                    aDEVS.my_input[inport] = self.inbags[inport][1]
                    self.transitioning[aDEVS] = 2
                    self.reschedule.add(aDEVS)
                self.transitioning = {ClassicDEVSWrapper(m): self.transitioning[m] 
                                      for m in self.transitioning}
                for m in self.transitioning:
                    m.server = self
                self.dc_altered = set()
                ]]>
            </body>
        </method>
        <!-- Trigger all transition functions. -->
        <method name="compute_transitions">
            <body>
                <![CDATA[
                self.new_states = {}
                for aDEVS in self.transitioning:
                    aDEVS.my_input = {key: pickle.loads(pickle.dumps(aDEVS.my_input[key], pickle.HIGHEST_PROTOCOL)) for key in aDEVS.my_input}
                    if self.transitioning[aDEVS] == 1:
                        aDEVS.state = aDEVS.intTransition()
                    elif self.transitioning[aDEVS] == 2:
                        aDEVS.elapsed = self.simulation_time[0] - aDEVS.time_last[0]
                        aDEVS.state = aDEVS.extTransition(aDEVS.my_input)
                    aDEVS.old_states.append((self.simulation_time, pickle.dumps(aDEVS.state)))
                    aDEVS.my_input = {}
                    self.new_states[aDEVS] = aDEVS.state
                ]]>
            </body>
        </method>
        <!-- Trigger all timeAdvance functions. -->
        <method name="compute_ta">
            <body>
                <![CDATA[
                self.new_tn = {}
                t, age = self.simulation_time
                for aDEVS in self.transitioning:
                    ta = aDEVS.timeAdvance()
                    aDEVS.time_last = self.simulation_time
                    aDEVS.time_next = (t + ta, 1 if ta else (age + 1))
                    self.new_tn[aDEVS] = aDEVS.time_next
                    trace(self.trace_file, self.transitioning[aDEVS], aDEVS)
                self.model.scheduler.massReschedule(self.reschedule)
                self.time_next = self.model.scheduler.readFirst()
                ]]>
            </body>
        </method>
        <method name="dsAddPort">
            <parameter name="port" />
            <body>
                <![CDATA[
                self.dc_altered.add(port)
                self.structural_changes.setdefault('CREATED_PORTS', []).append((port.getPortFullName(), port.is_input))
                ]]>
            </body>
        </method>
        <method name="dsRemovePort">
            <parameter name="port" />
            <body>
                <![CDATA[
                for iport in port.inline:
                    iport.outline = [p for p in iport.outline if p != port]
                    self.dsDisconnectPorts(iport, port);
                for oport in port.outline:
                    oport.inline = [p for p in oport.inline if p != port]
                    self.dsDisconnectPorts(port, oport);
                self.dc_altered.add(port)
                self.structural_changes.setdefault('DELETED_PORTS', []).append(port.getPortFullName())
                ]]>
            </body>
        </method>
        <method name="dsDisconnectPorts">
            <parameter name="p1" />
            <parameter name="p2" />
            <body>
                <![CDATA[
                self.dc_altered.add(p1)
                self.structural_changes.setdefault('DISCONNECTED_PORTS', []).append((p1.getPortFullName(), p2.getPortFullName()))
                ]]>
            </body>
        </method>
        <method name="dsConnectPorts">
            <parameter name="p1" />
            <parameter name="p2" />
            <body>
                <![CDATA[
                self.dc_altered.add(p1)
                self.structural_changes.setdefault('CONNECTED_PORTS', []).append((p1.getPortFullName(), p2.getPortFullName()))
                ]]>
            </body>
        </method>
        <method name="dsUnscheduleModel">
            <parameter name="model" />
            <body>
                <![CDATA[
                if isinstance(model, CoupledDEVS):
                    for m in model.component_set:
                        self.dsUnscheduleModel(m, False)
                elif isinstance(model, AtomicDEVS):
                    self.model.component_set.remove(model)
                    self.model.models.remove(model)
                    # The model is removed, so remove it from the scheduler
                    self.model.scheduler.unschedule(model)
                    self.model_ids[model.model_id] = None
                    self.model.local_model_ids.remove(model.model_id)
                else:
                    raise DEVSException("Unknown model to schedule: %s" % model)
                for port in model.IPorts:
                    self.dsRemovePort(port)
                for port in model.OPorts:
                    self.dsRemovePort(port)
                self.structural_changes.setdefault('DELETED_MODELS', []).append(model.getModelFullName())
                ]]>
            </body>
        </method>
        <method name="dsScheduleModel">
            <parameter name="model" />
            <body>
                <![CDATA[
                if isinstance(model, CoupledDEVS):
                    model.full_name = model.parent.full_name + "." + model.getModelName()
                    for m in model.component_set:
                        self.dsScheduleModel(m)
                elif isinstance(model, AtomicDEVS):
                    model.model_id = len(self.model_ids)
                    model.full_name = model.parent.full_name + "." + model.getModelName()
                    self.model_ids.append(model)
                    self.model.component_set.append(model)
                    self.model.models.append(model)
                    self.model.local_model_ids.add(model.model_id)
                    model.time_last = (self.simulation_time[0] - model.elapsed, 1)
                    ta = model.timeAdvance()
                    if ta < 0:
                        raise DEVSException("Negative time advance in atomic model '" + \
                                            model.getModelFullName() + "' with value " + \
                                            str(ta) + " at initialisation")
                    model.time_next = (model.time_last[0] + ta, 1)
                    model.old_states.append((self.simulation_time, pickle.dumps(model.state)))
                    p = model.parent
                    model.select_hierarchy = [model]
                    while p != None:
                        model.select_hierarchy = [p] + model.select_hierarchy
                        p = p.parent
                    if model.time_next[0] == self.simulation_time[0]:
                        model.time_next = (model.time_next[0], self.simulation_time[1])
                        # If scheduled for 'now', update the age manually
                    # It is a new model, so add it to the scheduler too
                    self.model.scheduler.schedule(model)
                else:
                    raise DEVSException("Unknown model to schedule: %s" % model)
                self.structural_changes.setdefault('CREATED_MODELS', []).append(model.getModelFullName())
                self.structural_changes.setdefault('CREATED_PORTS', []).extend([(port.getPortFullName(), port.is_input) for port in model.ports.itervalues()])
                ]]>
            </body>
        </method>
        <method name="getSelfProxy">
            <body>
                <![CDATA[
                return self
                ]]>
            </body>
        </method>
        <!-- Process dynamic structure changes. -->
        <method name="process_structure_changes">
            <body>
                <![CDATA[
                dsdevs_dict = {}
                iterlist = [aDEVS.parent for aDEVS in self.transitioning 
                                         if aDEVS.modelTransition(dsdevs_dict)]
                # Contains all models that are already checked, to prevent duplicate checking.
                # This was not necessary for atomic models, as they are guaranteed to only be called
                # once, as they have no children to induce a structural change on them
                checked = set()
                while iterlist:
                    new_iterlist = []
                    for cDEVS in iterlist:
                        cDEVS.server = self
                        if cDEVS is None:
                            # Problematic
                            #assert warning("Root DEVS returned True in the modelTransition method; ignoring")
                            continue
                        if cDEVS in checked:
                            continue
                        checked.add(cDEVS)
                        if cDEVS.modelTransition(dsdevs_dict):
                            new_iterlist.append(cDEVS.parent)
                        cDEVS.server = None
                    # Don't update the iterlist while we are iterating over it
                    iterlist = new_iterlist
                if self.dc_altered:
                    self.model.redoDirectConnection(self.dc_altered)
                for m in self.transitioning:
                    m.server = None
                ]]>
            </body>
        </method>
        <method name="flush_file">
            <body>
                <![CDATA[
                if self.trace_file is not None:
                    self.trace_file.flush()
                ]]>
            </body>
        </method>
        <method name="process_breakpoints">
            <parameter name="realtime"/>
            <body>
                <![CDATA[
                breakpoint_id = self.should_terminate(realtime)
                for breakpoint in self.breakpoints:
                    if breakpoint.id == breakpoint_id:
                        if breakpoint.disable_on_trigger:
                            breakpoint.enabled = False
                        return breakpoint_id
                ]]>
            </body>
        </method>
        <method name="compute_timeNext">
            <body>
                <![CDATA[
                model_timeNext = self.model.scheduler.readFirst()
                if len(self.inject_queue) > 0:
                    self.time_next = min(model_timeNext, self.inject_queue[0]["time"])
                else:
                    self.time_next = model_timeNext
                ]]>
            </body>
        </method>
        <method name="rollback_step">
            <body>
                <![CDATA[
                    if len(self.transition_times) == 0:
                        return
                    new_time = self.transition_times.pop()
                    for model in self.model.component_set:
                        if model.old_states[-1][0] == new_time:
                            # Remove the current state
                            del model.old_states[-1]
                            # Set the new (old...) state
                            new_state = model.old_states[-1]
                            model.state = pickle.loads(new_state[1])
                            model.time_last = new_state[0]
                            ta = model.timeAdvance()
                            model.time_next = (model.time_last[0] + ta, model.time_last[1] + 1 if ta == 0 else 0)
                            self.model.scheduler.massReschedule([model])
                    self.simulation_time = self.transition_times[-1] if len(self.transition_times) > 0 else (0.0, 0)
                ]]>
            </body>
        </method>
        <scxml initial="initializing">
            <state id="initializing">
                <transition target="../main">
                    <script>
                        <![CDATA[
                        # Direct connection
                        if isinstance(self.model, CoupledDEVS):
                            self.model.component_set = directConnect(self.model.component_set, self.listeners)
                            self.model = RootDEVS(self.model.component_set, self.model.component_set, None)
                            self.model.listeners = self.listeners
                            for m in self.model.component_set:
                                m.time_last = (-m.elapsed, 1)
                                ta = m.timeAdvance()
                                m.time_next = (m.time_last[0] + ta, 1)
                                m.old_states = [(m.time_last, pickle.dumps(m.state))]
                        elif isinstance(self.model, AtomicDEVS):
                            for p in self.model.IPorts:
                                p.routing_inline = []
                                p.routing_outline = []
                            for p in self.model.OPorts:
                                p.routing_inline = []
                                p.routing_outline = []
                            self.model = RootDEVS([self.model], [self.model], None)
                            self.model.time_last = (-self.model.elapsed, 1)
                            ta = self.model.timeAdvance()
                            self.model.time_next = (self.model.time_last[0] + ta, 1)
                        # Fixed configuration options
                        self.model.scheduler = Scheduler(self.model.component_set, 1e-6, len(self.model.component_set))
                        self.time_next = self.model.scheduler.readFirst()
                        # Cached values
                        self.imminents = None
                        self.outbag = None
                        self.inbags = None
                        self.transitioning = None
                        self.new_states = None
                        self.new_tn = None
                        # Verbose trace file
                        self.trace_file = None
                        # Breakpoint management
                        self.breakpoints = []
                        # For a reset
                        # self.save_model = {model: (model.elapsed, pickle.dumps(model.state, pickle.HIGHEST_PROTOCOL)) for model in self.model.component_set}
                        # self.save_model = pickle.dumps(self.model, pickle.HIGHEST_PROTOCOL)
                        self.transition_times = []
                        self.interrupt_string = None
                        ]]>
                    </script>
                </transition>
            </state>
        
            <!-- Parallel states: one of them controls the simulation flow, the other the type of simulation being performed. -->
            <parallel id="main">
                <!-- When an injection is received, just append it to the list of pending injections.
                     These will be processed as soon as the current simulation step is finished.
                     Afterwards, return to the previous state, as there was no change of state. -->
                <state id="injection_monitor">
                    <state id="inject">
                        <transition port="request" event="inject" target=".">
                            <parameter name="configuration"/>
                            <script>
                                configuration["time"] = (configuration["time"], 1)
                                self.inject_queue.append(configuration)
                                self.inject_queue.sort(key=lambda i: i["time"])
                            </script>
                            <raise scope="output" port="reply" event="inject_ok"/>
                        </transition>
                    </state>
                </state>
                <state id="tracer_monitor">
                    <state id="trace">
                        <transition port="request" event="trace" target=".">
                            <parameter name="filename"/>
                            <script>
                                if filename is not None:
                                    self.trace_file = open(filename, 'w')
                                else:
                                    self.trace_file = None
                            </script>
                            <raise scope="output" port="reply" event="trace_config_ok"/>
                        </transition>
                    </state>
                </state>
                <state id="realtime_interrupt_monitor">
                    <state id="realtime_interrupt">
                        <transition target="." port="request" event="realtime_interrupt">
                            <parameter name="interrupt_string" />
                            <script>
                                self.interrupt_string = interrupt_string
                            </script>
                        </transition>
                    </state>
                </state>
                <!-- The main parallel component: the simulation flow. -->
                <state id="simulation_flow" initial="initialize">
                    <state id="initialize">
                        <onentry>
                            <script>
                                self.structural_changes = {'CREATED_MODELS': [], 'CREATED_PORTS': [], 'CONNECTED_PORTS': []}
                                queue = [self.root_model]
                                while queue:
                                    item = queue.pop()
                                    self.structural_changes['CREATED_MODELS'].append(item.getModelFullName())
                                    self.structural_changes['CREATED_PORTS'].extend([(port.getPortFullName(), port.is_input) for port in item.ports.itervalues()])
                                    self.structural_changes['CONNECTED_PORTS'].extend([(outport.getPortFullName(), inport.getPortFullName()) for outport in item.OPorts for inport in outport.outline])
                                    if isinstance(item, CoupledDEVS):
                                        self.structural_changes['CONNECTED_PORTS'].extend([(inport.getPortFullName(), inport2.getPortFullName()) for inport in item.IPorts for inport2 in inport.outline])
                                        queue.extend(item.component_set)
                            </script>
                        </onentry>
                        <transition target="../check_termination">
                            <raise scope="output" port="reply" event="all_states_reset">
                                <parameter expr="self.simulation_time"/>
                                <parameter expr="{m.getModelFullName(): (m.time_next, m.state) for m in self.model.component_set}"/>
                                <parameter expr="self.structural_changes"/>
                                <parameter expr="self.breakpoints" />
                            </raise>
                            <script>
                                self.structural_changes = {}
                            </script>
                        </transition>
                    </state>
                    <state id="check_termination" initial="workaround">
                        <onexit>
                            <script>
                                self.simulation_time = self.time_next
                            </script>
                        </onexit>

                        <state id="workaround">
                            <transition after="0" target="../check_termination"/>
                        </state>

                        <state id="wait">
                            <onexit>
                                <script>
                                    diff = time.time() - self.realtime_starttime
                                    self.simulation_time = (diff / self.realtime_scale, 1)
                                </script>
                            </onexit>
                            <transition after="self.calculate_after()" target="../check_termination"/>
                            <transition cond="INSTATE('../../../simulation_state/paused')" target="../check_termination"/>
                        </state>

                        <state id="small_step_check">
                            <transition cond="self.should_terminate(False) == -2" target="../../do_simulation"/>
                            <transition cond="self.should_terminate(False) == -1" target="../check_termination">
                                <raise event="termination_condition"/>
                            </transition>
                            <transition cond="self.should_terminate(False) &gt; -1" target="../check_termination">
                                <script>
                                    breakpoint_id = self.process_breakpoints(False)
                                </script>
                                <raise scope="output" port="reply" event="breakpoint_triggered">
                                    <parameter expr="breakpoint_id"/>
                                    <parameter expr="self.serialize('transitioning', self.transitioning)"/>
                                    <parameter expr="self.serialize('outbag', self.outbag)"/>
                                    <parameter expr="self.serialize('inbags', self.inbags)"/>
                                </raise>
                                <raise event="termination_condition"/>
                            </transition>
                        </state>

                        <state id="check_termination">
                            <onentry>
                                <script>
                                    self.compute_timeNext()
                                    self.the_time = self.calculate_after()
                                </script>
                            </onentry>
                            <!-- Continue simulation -->
                            <transition cond="INSTATE('../../../simulation_state/continuous') and (self.should_terminate(False) == -2)" target="../../do_simulation"/>
                            <transition cond="INSTATE('../../../simulation_state/big_step') and (self.should_terminate(False) == -2)" target="../../do_simulation"/>

                            <!-- Realtime and didn't reach the required time_next yet -->
                            <transition cond="INSTATE('../../../simulation_state/realtime') and (self.should_terminate(True) == -2) and (self.the_time &gt; 0.0)" target="../wait"/>

                            <transition cond="INSTATE('../../../simulation_state/realtime') and (self.should_terminate(True) == -2) and (self.the_time &lt;= 0.0)" target="../../do_simulation"/>

                            <transition port="request" event="small_step" target="../small_step_check">
                                <parameter name="configuration" type="dict" default="{}"/>
                                <script>
                                    self.parse_options(configuration)
                                        
                                    self.listeners = {getattr(self.root_model, portname): listener for (portname, listener) in self.listeners_by_string.iteritems()}
                                    if not (len(self.listeners) == len(self.model.listeners)):
                                        self.model.listeners = self.listeners
                                        self.model.component_set = directConnect(self.model.models, self.listeners)
                                </script>
                            </transition>

                            <!-- Pause simulation -->
                            <transition cond="(not INSTATE('../../../simulation_state/paused') and INSTATE('../../../simulation_state/realtime') and (self.should_terminate(True) == -1))" target="../workaround">
                                <raise event="termination_condition"/>
                            </transition>
                            <transition cond="(not INSTATE('../../../simulation_state/paused') and not INSTATE('../../../simulation_state/realtime') and (self.should_terminate(False) == -1))" target="../workaround">
                                <raise event="termination_condition"/>
                            </transition>
                            <transition cond="(not INSTATE('../../../simulation_state/paused')) and INSTATE('../../../simulation_state/realtime') and (self.should_terminate(True) &gt; -1)" target="../workaround">
                                <script>
                                    breakpoint_id = self.process_breakpoints(True)
                                </script>
                                <raise scope="output" port="reply" event="breakpoint_triggered">
                                    <parameter expr="breakpoint_id"/>
                                    <parameter expr="self.serialize('transitioning', self.transitioning)"/>
                                    <parameter expr="self.serialize('outbag', self.outbag)"/>
                                    <parameter expr="self.serialize('inbags', self.inbags)"/>
                                </raise>
                                <raise event="termination_condition"/>
                            </transition>
                            <transition cond="(not INSTATE('../../../simulation_state/paused')) and not INSTATE('../../../simulation_state/realtime') and (self.should_terminate(False) &gt; -1)" target="../workaround">
                                <script>
                                    breakpoint_id = self.process_breakpoints(False)
                                </script>
                                <raise scope="output" port="reply" event="breakpoint_triggered">
                                    <parameter expr="breakpoint_id"/>
                                    <parameter expr="self.serialize('transitioning', self.transitioning)"/>
                                    <parameter expr="self.serialize('outbag', self.outbag)"/>
                                    <parameter expr="self.serialize('inbags', self.inbags)"/>
                                </raise>
                                <raise event="termination_condition"/>
                            </transition>

                            <!-- Process god event -->
                            <transition cond="INSTATE('../../../simulation_state/paused')" port="request" event="god_event" target="../workaround">
                                <parameter name="configuration"/>
                                <script>
                                    modelname = configuration["model"]
                                    state_attribute = configuration["attribute"]
                                    new_value = configuration["value"]
                                    model = self.find_model_with_name(modelname)
                                    setattr(model.state, state_attribute, new_value)
                                    # Call the timeadvance method again and compute new ta
                                    ta = model.timeAdvance()
                                    model.time_next = (model.time_last[0] + ta, 1 if ta else (model.time_last[1] + 1))
                                    self.model.scheduler.massReschedule([model])
                                    # self.simulation_time = self.model.scheduler.readFirst()
                                </script>
                                <raise scope="output" port="reply" event="god_event_ok">
                                    <parameter expr="{model.getModelFullName(): model.state}"/>
                                </raise>
                                <raise scope="output" port="reply" event="new_tn">
									<parameter expr="self.simulation_time"/>
                                    <parameter expr="{model.getModelFullName(): model.time_next}"/>
                                </raise>
                            </transition>

                        </state>
                    </state>

                    <state id="do_simulation" initial="init">
                        <state id="init">
                            <onexit>
                                <script>
                                    self.find_internal_imminents()
                                </script>
                            </onexit>
                            <transition cond="not INSTATE('../../../simulation_state/paused')" target="../found_internal_imminents"/>
                            <!-- Always output this if paused, as we only got here because a small step was fired previously -->
                            <transition cond="INSTATE('../../../simulation_state/paused')" target="../found_internal_imminents">
                                <raise scope="output" port="reply" event="imminents">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.serialize('imminents', self.imminents)"/>
                                </raise>
                            </transition>
                        </state>

                        <state id="found_internal_imminents">
                            <onexit>
                                <script>
                                    self.select_imminent()
                                </script>
                            </onexit>
                            <transition cond="not INSTATE('../../../simulation_state/paused')" target="../selected_imminent"/>
                            <transition port="request" event="small_step" target="../selected_imminent">
                                <raise scope="output" port="reply" event="selected_imminent">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.serialize('imminent', self.imminent)"/>
                                </raise>
                            </transition>
                        </state>

                        <state id="selected_imminent">
                            <onexit>
                                <script>
                                    self.compute_outputfunction()
                                </script>
                            </onexit>
                            <transition cond="not INSTATE('../../../simulation_state/paused')" target="../computed_outputfunction"/>
                            <transition port="request" event="small_step" target="../computed_outputfunction">
                                <raise scope="output" port="reply" event="outbag">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.serialize('outbag', self.outbag)"/>
                                </raise>
                            </transition>
                        </state>

                        <state id="computed_outputfunction">
                            <onexit>
                                <script>
                                    self.route_messages()
                                    self.process_injects()
                                </script>
                            </onexit>
                            <transition cond="not INSTATE('../../../simulation_state/paused')" target="../routed_messages">
                            </transition>
                            <transition port="request" event="small_step" target="../routed_messages">
                                <raise scope="output" port="reply" event="inbags">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.serialize('inbags', self.inbags)"/>
                                </raise>
                            </transition>
                        </state>

                        <state id="routed_messages">
                            <onexit>
                                <script>
                                    self.find_all_imminents()
                                </script>
                            </onexit>
                            <transition cond="not INSTATE('../../../simulation_state/paused')" target="../found_all_imminents">
                            </transition>
                            <transition port="request" event="small_step" target="../found_all_imminents">
                                <raise scope="output" port="reply" event="transitioning">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.serialize('transitioning', self.transitioning)"/>
                                </raise>
                            </transition>
                        </state>

                        <state id="found_all_imminents">
                            <onexit>
                                <script>
                                    self.compute_transitions()
                                </script>
                            </onexit>
                            <transition cond="not INSTATE('../../../simulation_state/paused')" target="../computed_transitions"/>
                            <transition port="request" event="small_step" target="../computed_transitions">
                                <raise scope="output" port="reply" event="new_internal_states">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.serialize('new_states', self.new_states)"/>
                                </raise>
                            </transition>
                        </state>

                        <state id="computed_transitions">
                            <onexit>
                                <script>
                                    self.compute_ta()
                                </script>
                            </onexit>
                            <transition cond="not INSTATE('../../../simulation_state/paused')" target="../computed_ta"/>
                            <transition port="request" event="small_step" target="../computed_ta">
                                <raise scope="output" port="reply" event="new_tn">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.serialize('new_tn', self.new_tn)"/>
                                </raise>
                            </transition>
                        </state>

                        <state id="computed_ta">
                            <onexit>
                                <script>
                                    self.process_structure_changes()
                                </script>
                            </onexit>
                            <transition cond="INSTATE('../../../simulation_state/continuous')" target="../../check_termination"/>
                            <transition port="request" event="small_step" target="../../check_termination">
                                <raise scope="output" port="reply" event="structural_changes">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.structural_changes"/>
                                </raise>
                                <script>
                                    self.structural_changes = {}
                                </script>
                            </transition>
                            <transition cond="INSTATE('../../../simulation_state/realtime') or INSTATE('../../../simulation_state/big_step')" target="../../check_termination">
                                <raise event="big_step_done"/>
                                <raise scope="output" port="reply" event="new_states">
                                    <parameter expr="self.simulation_time"/>
                                    <parameter expr="self.serialize('new_states', self.new_states)"/>
                                    <parameter expr="self.serialize('new_tn', self.new_tn)"/>
                                    <parameter expr="self.structural_changes"/>
                                </raise>
                                <script>
                                    self.structural_changes = {}
                                </script>
                            </transition>
                        </state>
                    </state>
                </state>

                <state id="simulation_state" initial="paused">
                    <state id="paused">
                        <transition port="request" event="simulate" target="../continuous">
                            <parameter name="configuration" type="dict" default="{}"/>
                            <script>
                                self.parse_options(configuration)
                            </script>
                        </transition>
                        <transition port="request" event="realtime" target="../realtime">
                            <parameter name="configuration" type="dict" default="{}"/>
                            <script>
                                self.parse_options(configuration)
                            </script>
                        </transition>
                        <transition port="request" event="big_step" target="../big_step">
                            <parameter name="configuration" type="dict" default="{}"/>
                            <script>
                                self.parse_options(configuration)
                            </script>
                        </transition>
                    </state>
                    <state id="continuous">
                        <onentry>
                            <script>
                                self.listeners = {getattr(self.root_model, portname): listener for (portname, listener) in self.listeners_by_string.iteritems()}
                                self.model.listeners = self.listeners
                                self.model.component_set = directConnect(self.model.models, self.listeners)
                            </script>
                        </onentry>
                        <transition port="request" event="pause" target=".">
                            <script>
                                # Just override termination condition
                                self.termination_condition = lambda i, j, k : True
                                self.termination_time = None
                            </script>
                        </transition>
                        <transition event="termination_condition" target="../paused">
                            <raise port="reply" event="terminate">
                                <parameter expr="self.simulation_time"/>
                            </raise>
                            <script>
                                self.flush_file()
                            </script>
                            <raise scope="output" port="reply" event="all_states">
                                <parameter expr="self.simulation_time"/>
                                <parameter expr="{m.getModelFullName(): (m.time_next, m.state) for m in self.model.component_set}"/>
                                <parameter expr="self.structural_changes"/>
                            </raise>
                            <script>
                                self.structural_changes = {}
                            </script>
                        </transition>
                    </state>
                    <state id="realtime">
                        <onentry>
                            <script>
                                self.listeners = {getattr(self.root_model, portname): listener for (portname, listener) in self.listeners_by_string.iteritems()}
                                self.model.listeners = self.listeners
                                self.model.component_set = directConnect(self.model.models, self.listeners)
                            </script>
                        </onentry>
                        <transition port="request" event="pause" target=".">
                            <script>
                                # Just override termination condition
                                self.termination_condition = lambda i, j, k : True
                                self.termination_time = None
                            </script>
                        </transition>
                        <transition event="termination_condition" target="../paused">
                            <raise port="reply" event="terminate">
                                <parameter expr="self.simulation_time"/>
                            </raise>
                            <script>
                                self.flush_file()
                            </script>
                        </transition>
                    </state>
                    <state id="big_step">
                        <onentry>
                            <script>
                                self.listeners = {getattr(self.root_model, portname): listener for (portname, listener) in self.listeners_by_string.iteritems()}
                                self.model.listeners = self.listeners
                                self.model.component_set = directConnect(self.model.models, self.listeners)
                            </script>
                        </onentry>
                        <transition event="big_step_done" target="../paused"/>
                        <transition event="termination_condition" target="../paused">
                            <raise port="reply" event="terminate">
                                <parameter expr="self.simulation_time"/>
                            </raise>
                            <script>
                                self.flush_file()
                            </script>
                        </transition>
                    </state>
                </state>
                <state id="breakpoint_manager">
                    <state id="breakpoint_manage">
                        <transition port="request" event="add_breakpoint" target=".">
                            <parameter name="breakpoint_id"/>
                            <parameter name="function"/>
                            <parameter name="enabled"/>
                            <parameter name="disable_on_trigger"/>
                            <script>
                                # TODO: report to the user if a breakpoint is invalid (trying to include in scope and reporting any exceptions).
                                self.breakpoints.append(Breakpoint(breakpoint_id, function, enabled, disable_on_trigger))
                            </script>
                        </transition>
                        <transition port="request" event="del_breakpoint" target=".">
                            <parameter name="del_breakpoint_id"/>
                            <script>
                                self.breakpoints = [breakpoint for breakpoint in self.breakpoints if breakpoint.id != del_breakpoint_id]
                            </script>
                        </transition>
                        <transition port="request" event="toggle_breakpoint" target=".">
                            <parameter name="breakpoint_id"/>
                            <parameter name="enabled"/>
                            <script>
                                for breakpoint in self.breakpoints:
                                    if breakpoint.id == breakpoint_id:
                                        breakpoint.enabled = enabled
                                        break
                            </script>
                        </transition>
                    </state>
                </state>
                <state id="listeners">
                    <state id="listening">                    
                        <transition target="." event="set_listen_ports">
                            <parameter name="port" />
                            <parameter name="function" />
                            <script>
                                self.listeners_by_string[port] = function
                            </script>
                        </transition>
                    </state>
                </state>
                <state id="reset">
                    <state id="reset">
                        <transition port="request" event="reset" target="../../../initializing">
                            <script>
                                self.model = pickle.loads(self.save_model)
                                self.initialize_simulation()
                            </script>
                        </transition>
                    </state>
                </state>
            </parallel>
        </scxml>
    </class>
</diagram>