123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086 |
- <?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) > -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 > 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)
-
- 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) > -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) > -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>
|