12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082 |
- <?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 = {}
- # Simulation variables
- self.termination_time = None
- self.termination_condition = None
- self.simulation_time = (0.0, 0)
- 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=[])
- ]]>
- </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 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}
- ]]>
- </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 = 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.
- 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? -->
- <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)
- 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.setdefault(inport, []).append(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]
- 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.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="dsRemovePort">
- <parameter name="port" />
- <body>
- <![CDATA[
- for iport in port.inline:
- iport.outline = [p for p in iport.outline if p != port]
- for oport in port.outline:
- oport.inline = [p for p in oport.inline if p != port]
- self.dc_altered.add(port)
- self.structural_changes.setdefault('REMOVED_PORTS', []).append(port)
- ]]>
- </body>
- </method>
- <method name="dsDisonnectPorts">
- <parameter name="p1" />
- <parameter name="p2" />
- <body>
- <![CDATA[
- self.dc_altered.add(p1)
- self.structural_changes.setdefault('DISCONNECTED_PORTS', []).append((p1, p2))
- ]]>
- </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, p2))
- ]]>
- </body>
- </method>
- <method name="dsUnscheduleModel">
- <parameter name="model" />
- <body>
- <![CDATA[
- if isinstance(model, CoupledDEVS):
- for m in model.component_set:
- self.dsUnscheduleModel(m, False)
- for port in model.IPorts:
- self.dsRemovePort(port)
- for port in model.OPorts:
- self.dsRemovePort(port)
- 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)
- for port in model.IPorts:
- self.dsRemovePort(port)
- for port in model.OPorts:
- self.dsRemovePort(port)
- else:
- raise DEVSException("Unknown model to schedule: %s" % model)
- self.structural_changes.setdefault('DELETED_MODELS', []).append(model)
- ]]>
- </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)
- ]]>
- </body>
- </method>
- <method name="getSelfProxy">
- <body>
- <![CDATA[
- return self
- ]]>
- </body>
- </method>
- <!-- Process dynamic structure changes. -->
- <method name="process_structure_changes">
- <body>
- <![CDATA[
- self.structural_changes = {}
- self.dc_altered = set()
- dsdevs_dict = {}
- for m in self.transitioning:
- m.server = self
- 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">
- <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.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(): 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="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.structural_changes"/>
- </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)"/>
- <parameter expr="self.serialize('new_tn', self.new_tn)"/>
- <parameter expr="self.structural_changes"/>
- </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">
- <onentry>
- <script>
- #self.model.listeners = {}
- #self.model.directConnect()
- self.model.listeners = {}
- self.model.component_set = directConnect(self.model.models, {})
- </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}"/>
- </raise>
- </transition>
- </state>
- <state id="realtime">
- <onentry>
- <script>
- #self.model.listeners = self.listeners
- #self.model.directConnect()
- 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
- # 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">
- <onentry>
- <script>
- #self.model.listeners = self.listeners
- #self.model.directConnect()
- 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>
- 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.time_last = (-model.elapsed, 1)
- ta = model.timeAdvance()
- model.time_next = (model.time_last[0] + ta, 1)
- '''
- self.model_ids = []
- self.model = pickle.loads(self.save_model)
- self.simulation_time = (0.0, 0)
- print self.listeners
- self.model.listeners = self.listeners
- self.model.component_set = directConnect(self.model.models, self.listeners)
- 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-->
- <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_ids = []
- self.model = pickle.loads(self.save_model)
- # 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=[])
- print 'resetting'
- </script>
- </transition>
- </state>
- </state>
- </parallel>
- </scxml>
- </class>
- </diagram>
|