123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836 |
- <?xml version="1.0" ?>
- <diagram author="Yentl Van Tendeloo" name="DEVS simulator">
- <description>
- A restricted PythonPDEVS simulator modelled in SCCD for classic, dynamic structure DEVS models.
- </description>
- <top>
- import cPickle as pickle
- import time
- # 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[
- # Simulation variables
- self.termination_time = None
- self.termination_condition = None
- self.simulation_time = (0.0, 0)
- self.model = model
- self.listeners = {}
- self.root_model = 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=[])
- # 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)
- for m in self.model.component_set:
- m.timeLast = (-m.elapsed, 1)
- ta = m.timeAdvance()
- m.time_next = (m.timeLast[0] + ta, 1)
- m.old_states = [(m.timeLast, 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.timeLast = (-self.model.elapsed, 1)
- ta = self.model.timeAdvance()
- self.model.time_next = (self.model.timeLast[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.transition_times = []
- ]]>
- </body>
- </constructor>
- <!-- 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 == "inbags" or type == "outbag":
- return {m.getPortFullName(): object[m] for m in object}
- elif type == "new_tn" or type == "new_states":
- return {m.getModelFullName(): object[m] for m in object}
- elif type == "transitioning":
- return {m.getModelFullName(): {1: "INTERNAL", 2: "EXTERNAL"}[object[m]] for m in object}
- ]]>
- </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
- 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 = set(self.transitioning.keys())
- 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("
- if breakpoint(check_time, self.root_model, transitioned):
- # 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.setdefault(inport, []).extend(payload)
- ]]>
- </body>
- </method>
- <!-- Process the injects on the inject_queue.
- 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? -->
- <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)
- self.inbags.setdefault(port, []).append(event)
- ]]>
- </body>
- </method>
- <!-- Combine the information from the routed messages to determine the external, internal and confluent 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]
- self.transitioning[aDEVS] = 2
- self.reschedule.add(aDEVS)
- self.transitioning = {ClassicDEVSWrapper(m): self.transitioning[m]
- for m in self.transitioning}
- ]]>
- </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.timeLast[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.timeLast = 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="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.timeLast = new_state[0]
- ta = model.timeAdvance()
- model.time_next = (model.timeLast[0] + ta, model.timeLast[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="main">
- <!-- 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>
- <!-- The main parallel component: the simulation flow. -->
- <state id="simulation_flow" initial="initialize">
- <state id="initialize">
- <transition target="../check_termination">
- <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}"/>
- </raise>
- </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) > -1" target="../check_termination">
- <script>
- breakpoint_id = self.process_breakpoints(False)
- </script>
- <raise scope="output" port="reply" event="breakpoint_triggered">
- <parameter expr="breakpoint_id"/>
- </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 > 0.0)" target="../wait"/>
- <transition cond="INSTATE('../../../simulation_state/realtime') and (self.should_terminate(True) == -2) and (self.the_time <= 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)
- </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) > -1)" target="../workaround">
- <script>
- breakpoint_id = self.process_breakpoints(True)
- </script>
- <raise scope="output" port="reply" event="breakpoint_triggered">
- <parameter expr="breakpoint_id"/>
- </raise>
- <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">
- <script>
- breakpoint_id = self.process_breakpoints(False)
- </script>
- <raise scope="output" port="reply" event="breakpoint_triggered">
- <parameter expr="breakpoint_id"/>
- </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.timeLast[0] + ta, 1 if ta else (model.timeLast[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(): str(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>
- <!-- Omniscient debugging -->
- <transition cond="INSTATE('../../../simulation_state/paused')" port="request" event="backwards_step" target="../workaround">
- <script>
- self.rollback_step()
- </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}"/>
- </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_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="INSTATE('../../../simulation_state/continuous')" target="../../check_termination"/>
- <transition port="request" event="small_step" target="../../check_termination">
- <raise scope="output" port="reply" event="new_tn">
- <parameter expr="self.simulation_time"/>
- <parameter expr="self.serialize('new_tn', self.new_tn)"/>
- </raise>
- </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)"/>
- </raise>
- <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>
- </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">
- <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}"/>
- </raise>
- </transition>
- </state>
- <state id="realtime">
- <transition port="request" event="pause" target=".">
- <script>
- # Just override termination condition
- self.termination_condition = lambda i, j, k : True
- self.termination_time = None
- # Don't forget to correctly set the simulation time
- diff = time.time() - self.realtime_starttime
- self.simulation_time = (diff / self.realtime_scale, 1)
- </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">
- <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>
- 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="reset">
- <state id="reset">
- <transition port="request" event="reset" target=".">
- <script>
- for model in self.model.component_set:
- model.state = pickle.loads(self.save_model[model][1])
- model.elapsed = self.save_model[model][0]
- model.timeLast = (-model.elapsed, 1)
- ta = model.timeAdvance()
- model.time_next = (model.timeLast[0] + ta, 1)
- self.simulation_time = (0.0, 0)
- self.model.scheduler.massReschedule(self.model.component_set)
- # Reset trace file
- if self.trace_file is not None:
- self.trace_file = open(self.trace_file.name, 'w')
- </script>
- <raise scope="output" port="reply" event="all_states">
- <parameter expr="(0.0, 0)"/>
- <parameter expr="{m.getModelFullName(): (m.time_next, m.state) for m in self.model.component_set}"/>
- </raise>
- </transition>
- </state>
- </state>
- </parallel>
- </scxml>
- </class>
- </diagram>
|