class RuntimeClassBase(object): __metaclass__ = abc.ABCMeta def __init__(self): self.active = False self.state_changed = False self.events = EventQueue(); self.timers = None; def addEvent(self, event, time_offset = 0.0): self.events.add(event, time_offset); def getEarliestEventTime(self) : if self.timers : return min(self.events.getEarliestTime(), min(self.timers.itervalues())) return self.events.getEarliestTime(); def step(self, delta): if not self.active : return self.events.decreaseTime(delta); if self.timers : next_timers = {} for (key,value) in self.timers.iteritems() : time = value - delta if time <= 0.0 : self.addEvent( Event("_" + str(key) + "after"), time); else : next_timers[key] = time self.timers = next_timers; self.microstep() while (self.state_changed) : self.microstep() def microstep(self): due = self.events.popDueEvents() if (len(due) == 0) : self.transition() else : for event in due : self.transition(event); @abc.abstractmethod def transition(self, event = None): pass def start(self): self.active = True def stop(self): self.active = False
class ControllerBase(object): def __init__(self, object_manager, keep_running): self.object_manager = object_manager self.keep_running = keep_running # Keep track of input ports self.input_ports = [] self.input_queue = EventQueue(); # Keep track of output ports self.output_ports = [] self.output_listeners = [] # Let statechart run one last time before stopping self.done = False def addInputPort(self, port_name): self.input_ports.append(port_name) def addOutputPort(self, port_name): self.output_ports.append(port_name) def broadcast(self, new_event): self.object_manager.broadcast(new_event) def start(self): self.object_manager.start() def stop(self): pass def addInput(self, input_event, time_offset = 0.0): if input_event.getName() == "" : raise InputException("Input event can't have an empty name.") if input_event.getPort() not in self.input_ports : raise InputException("Input port mismatch.") self.input_queue.add(input_event, time_offset) def outputEvent(self, event): for listener in self.output_listeners : listener.add(event) def addOutputListener(self, ports): listener = OutputListener(ports) self.output_listeners.append(listener) return listener def addEventList(self, event_list): for (event, time_offset) in event_list : self.addInput(event, time_offset) def getObjectManager(self): return self.object_manager;
class ObjectManagerBase(object): __metaclass__ = abc.ABCMeta def __init__(self, controller): self.controller = controller self.events = EventQueue() self.instances_map = {} #a dictionary that maps RuntimeClassBase to InstanceWrapper def addEvent(self, event, time_offset = 0.0): self.events.add(event, time_offset) # Broadcast an event to all instances def broadcast(self, new_event): for i in self.instances_map: i.addEvent(new_event) def getWaitTime(self): #first get waiting time of the object manager's events smallest_time = self.events.getEarliestTime(); #check all the instances for instance in self.instances_map.iterkeys() : smallest_time = min(smallest_time, instance.getEarliestEventTime()) return smallest_time; def stepAll(self, delta): self.step(delta) for i in self.instances_map.iterkeys(): i.step(delta) def step(self, delta): self.events.decreaseTime(delta); for event in self.events.popDueEvents() : self.handleEvent(event) def start(self): for i in self.instances_map: i.start() def handleEvent(self, e): if e.getName() == "narrow_cast" : self.handleNarrowCastEvent(e.getParameters()) elif e.getName() == "broad_cast" : self.handleBroadCastEvent(e.getParameters()) elif e.getName() == "create_instance" : self.handleCreateEvent(e.getParameters()) elif e.getName() == "associate_instance" : self.handleAssociateEvent(e.getParameters()) elif e.getName() == "start_instance" : self.handleStartInstanceEvent(e.getParameters()) elif e.getName() == "delete_instance" : self.handleDeleteInstanceEvent(e.getParameters()) def processAssociationReference(self, input_string): if len(input_string) == 0 : raise AssociationReferenceException("Empty association reference.") regex_pattern = re.compile("^([a-zA-Z_]\w*)(?:\[(\d+)\])?$"); path_string = input_string.split("/") result = [] for piece in path_string : match = regex_pattern.match(piece) if match : name = match.group(1) index = match.group(2) if index is None : index = -1 result.append((name,int(index))) else : raise AssociationReferenceException("Invalid entry in association reference.") return result def handleStartInstanceEvent(self, parameters): if len(parameters) != 2 : raise ParameterException ("The start instance event needs 2 parameters.") else : source = parameters[0] traversal_list = self.processAssociationReference(parameters[1]) for i in self.getInstances(source, traversal_list) : i.instance.start() source.addEvent(Event("instance_started", parameters = [parameters[1]])) def handleBroadCastEvent(self, parameters): if len(parameters) != 1 : raise ParameterException ("The broadcast event needs 1 parameter.") self.broadcast(parameters[0]) def handleCreateEvent(self, parameters): if len(parameters) < 2 : raise ParameterException ("The create event needs at least 2 parameters.") else : source = parameters[0] association_name = parameters[1] association = self.instances_map[source].getAssociation(association_name) if association.allowedToAdd() : ''' allow subclasses to be instantiated ''' class_name = association.class_name if len(parameters) == 2 else parameters[2] new_instance_wrapper = self.createInstance(class_name, parameters[3:]) idx = association.addInstance(new_instance_wrapper) try: new_instance_wrapper.getAssociation('parent').addInstance(self.instances_map[source]) except AssociationReferenceException: pass source.addEvent(Event("instance_created", parameters = ['%s[%i]' % (association_name, idx)])) else : source.addEvent(Event("instance_creation_error", parameters = [association_name])) def handleDeleteInstanceEvent(self, parameters): if len(parameters) < 2 : raise ParameterException ("The delete event needs at least 2 parameters.") else : source = parameters[0] association_name = parameters[1] traversal_list = self.processAssociationReference(association_name) instances = self.getInstances(source, traversal_list) association = self.instances_map[source].getAssociation(traversal_list[0][0]) for i in instances: association.removeInstance(i) i.getInstance().stop() if hasattr(i.instance, '__del__'): i.instance.__del__() source.addEvent(Event("instance_deleted", parameters = [parameters[1]])) def handleAssociateEvent(self, parameters): if len(parameters) != 3 : raise ParameterException ("The associate_instance event needs 3 parameters."); else : source = parameters[0] to_copy_list = self.getInstances(source,self.processAssociationReference(parameters[1])) if len(to_copy_list) != 1 : raise AssociationReferenceException ("Invalid source association reference.") wrapped_to_copy_instance = to_copy_list[0] dest_list = self.processAssociationReference(parameters[2]) if len(dest_list) == 0 : raise AssociationReferenceException ("Invalid destination association reference.") last = dest_list.pop() if last[1] != -1 : raise AssociationReferenceException ("Last association name in association reference should not be accompanied by an index.") for i in self.getInstances(source, dest_list) : i.getAssociation(last[0]).addInstance(wrapped_to_copy_instance) def handleNarrowCastEvent(self, parameters): if len(parameters) != 3 : raise ParameterException ("The associate_instance event needs 3 parameters.") source = parameters[0] traversal_list = self.processAssociationReference(parameters[1]) cast_event = parameters[2] for i in self.getInstances(source, traversal_list) : i.instance.addEvent(cast_event) def getInstances(self, source, traversal_list): currents = [self.instances_map[source]] for (name, index) in traversal_list : nexts = [] for current in currents : association = current.getAssociation(name) if (index >= 0 ) : nexts.append ( association.getInstance(index) ); elif (index == -1) : nexts.extend ( association.instances.values() ); else : raise AssociationReferenceException("Incorrect index in association reference.") currents = nexts return currents @abc.abstractmethod def instantiate(self, class_name, construct_params): pass def createInstance(self, class_name, construct_params = []): instance_wrapper = self.instantiate(class_name, construct_params) if instance_wrapper: self.instances_map[instance_wrapper.getInstance()] = instance_wrapper return instance_wrapper
class RuntimeClassBase(object): __metaclass__ = abc.ABCMeta def __init__(self, controller): self.active = False self.is_stable = True self.events = EventQueue() self.controller = controller self.timers = None self.inports = {} self.semantics = StatechartSemantics() def start(self): self.current_state = {} self.history_state = {} self.timers = {} self.big_step = BigStepState() self.combo_step = ComboStepState() self.small_step = SmallStepState() self.active = True self.is_stable = False self.initializeStatechart() self.processBigStepOutput() def stop(self): self.active = False def addEvent(self, event_list, time_offset=0.0): if not isinstance(event_list, list): event_list = [event_list] self.events.add(event_list, time_offset) def getEarliestEventTime(self): if not self.active: return INFINITY if not self.is_stable: return 0.0 if self.timers: return min(self.events.getEarliestTime(), min(self.timers.itervalues())) return self.events.getEarliestTime() def processBigStepOutput(self): for e in self.big_step.getOutputEvents(): self.controller.outputEvent(e) for e in self.big_step.getOutputEventsOM(): self.controller.object_manager.addEvent(e) def step(self, delta): if not self.active: return # decrease event queue time self.events.decreaseTime(delta) # decrease timers time next_timers = {} for (key, value) in self.timers.iteritems(): time = value - delta if time <= 0.0: self.addEvent(Event("_" + str(key) + "after"), time) else: next_timers[key] = time self.timers = next_timers # execute big step(s) due = self.events.popDueEvents() if not due and not self.is_stable: due = [[]] for input_events in due: # perform 1 big step per slot in 'due' self.is_stable = not self.bigStep(input_events) self.processBigStepOutput() def inState(self, nodes): for c in self.current_state.itervalues(): new_nodes = [] for n in nodes: if not (n in c): new_nodes.append(n) nodes = new_nodes if len(nodes) == 0: return True return False def bigStep(self, input_events): #print "new big step" self.big_step.next(input_events) self.small_step.reset() self.combo_step.reset() while self.comboStep(): self.big_step.setStepped() if self.semantics.big_step_maximality == StatechartSemantics.TakeOne: break # Take One -> only one combo step allowed return self.big_step.hasStepped() def comboStep(self): #print "new combo step" self.combo_step.next() while self.smallStep(): self.combo_step.setStepped() return self.combo_step.hasStepped() def smallStep(self): if self.small_step.hasStepped(): self.small_step.next() self.generateCandidates() if self.small_step.hasCandidates(): #print "new small step, have " + str(len(self.small_step.getCandidates())) + " candidates" if self.semantics.concurrency == StatechartSemantics.Single: transition, parameters = self.small_step.getCandidates()[ 0] # execute first of candidates transition(parameters) elif self.semantics.concurrency == StatechartSemantics.Many: pass # TODO: implement self.small_step.setStepped() return self.small_step.hasStepped() def getEnabledEvents(self): result = self.small_step.getCurrentEvents( ) + self.combo_step.getCurrentEvents() if self.semantics.input_event_lifeline == StatechartSemantics.Whole or ( not self.big_step.hasStepped() and (self.semantics.input_event_lifeline == StatechartSemantics.FirstComboStep or (not self.combo_step.hasStepped() and self.semantics.input_event_lifeline == StatechartSemantics.FirstSmallStep))): result += self.big_step.getInputEvents() return result def raiseInternalEvent(self, event): if self.semantics.internal_event_lifeline == StatechartSemantics.NextSmallStep: self.small_step.addNextEvent(event) elif self.semantics.internal_event_lifeline == StatechartSemantics.NextComboStep: self.combo_step.addNextEvent(event) elif self.semantics.internal_event_lifeline == StatechartSemantics.Queue: self.events.add([event], 0.0) @abc.abstractmethod def initializeStatechart(self): pass @abc.abstractmethod def generateCandidates(self): pass
class ControllerBase(object): def __init__(self, object_manager): self.object_manager = object_manager self.private_port_counter = 0 # Keep track of input ports self.input_ports = {} self.input_queue = EventQueue() # Keep track of output ports self.output_ports = [] self.output_listeners = [] # Let statechart run one last time before stopping self.done = False def addInputPort(self, virtual_name, instance=None): if instance == None: port_name = virtual_name else: port_name = "private_" + str( self.private_port_counter) + "_" + virtual_name self.private_port_counter += 1 self.input_ports[port_name] = InputPortEntry(virtual_name, instance) return port_name def addOutputPort(self, port_name): self.output_ports.append(port_name) def broadcast(self, new_event): self.object_manager.broadcast(new_event) def start(self): self.object_manager.start() def stop(self): pass def addInput(self, input_event_list, time_offset=0.0): if not isinstance(input_event_list, list): input_event_list = [input_event_list] for e in input_event_list: if e.getName() == "": raise InputException("Input event can't have an empty name.") if e.getPort() not in self.input_ports: raise InputException("Input port mismatch, no such port: " + e.getPort() + ".") self.input_queue.add(input_event_list, time_offset) def getWaitTime(self): return min(self.object_manager.getWaitTime(), self.input_queue.getEarliestTime()) def handleInput(self, delta): self.input_queue.decreaseTime(delta) for events in self.input_queue.popDueEvents(): for e in events: input_port = self.input_ports[e.getPort()] e.port = input_port.virtual_name target_instance = input_port.instance if target_instance == None: self.broadcast(e) else: target_instance.addEvent(e) def outputEvent(self, event): for listener in self.output_listeners: listener.add(event) def addOutputListener(self, ports): listener = OutputListener(ports) self.output_listeners.append(listener) return listener def addMyOwnOutputListener(self, listener): self.output_listeners.append(listener) # deprecated, to add multiple events, use addInput instead def addEventList(self, event_list): for (event, time_offset) in event_list: self.addInput(event, time_offset) def getObjectManager(self): return self.object_manager
class ObjectManagerBase(object): __metaclass__ = abc.ABCMeta def __init__(self, controller): self.controller = controller self.events = EventQueue() self.instances = [ ] #a dictionary that maps RuntimeClassBase to InstanceWrapper def addEvent(self, event, time_offset=0.0): self.events.add(event, time_offset) # Broadcast an event to all instances def broadcast(self, new_event): for i in self.instances: i.addEvent(new_event) def getWaitTime(self): #first get waiting time of the object manager's events smallest_time = self.events.getEarliestTime() #check all the instances for instance in self.instances: smallest_time = min(smallest_time, instance.getEarliestEventTime()) return smallest_time def stepAll(self, delta): self.step(delta) for i in self.instances: i.step(delta) def step(self, delta): self.events.decreaseTime(delta) for event in self.events.popDueEvents(): self.handleEvent(event) def start(self): for i in self.instances: i.start() def handleEvent(self, e): if e.getName() == "narrow_cast": self.handleNarrowCastEvent(e.getParameters()) elif e.getName() == "broad_cast": self.handleBroadCastEvent(e.getParameters()) elif e.getName() == "create_instance": self.handleCreateEvent(e.getParameters()) elif e.getName() == "associate_instance": self.handleAssociateEvent(e.getParameters()) elif e.getName() == "start_instance": self.handleStartInstanceEvent(e.getParameters()) elif e.getName() == "delete_instance": self.handleDeleteInstanceEvent(e.getParameters()) def processAssociationReference(self, input_string): if len(input_string) == 0: raise AssociationReferenceException("Empty association reference.") regex_pattern = re.compile("^([a-zA-Z_]\w*)(?:\[(\d+)\])?$") path_string = input_string.split("/") result = [] for piece in path_string: match = regex_pattern.match(piece) if match: name = match.group(1) index = match.group(2) if index is None: index = -1 result.append((name, int(index))) else: raise AssociationReferenceException( "Invalid entry in association reference. Input string: " + input_string) return result def handleStartInstanceEvent(self, parameters): if len(parameters) != 2: raise ParameterException( "The start instance event needs 2 parameters.") else: source = parameters[0] traversal_list = self.processAssociationReference(parameters[1]) for i in self.getInstances(source, traversal_list): i["instance"].start() source.addEvent( Event("instance_started", parameters=[parameters[1]])) def handleBroadCastEvent(self, parameters): if len(parameters) != 1: raise ParameterException("The broadcast event needs 1 parameter.") self.broadcast(parameters[0]) def handleCreateEvent(self, parameters): if len(parameters) < 2: raise ParameterException( "The create event needs at least 2 parameters.") source = parameters[0] association_name = parameters[1] association = source.associations[association_name] #association = self.instances_map[source].getAssociation(association_name) if association.allowedToAdd(): ''' allow subclasses to be instantiated ''' class_name = association.to_class if len( parameters) == 2 else parameters[2] new_instance = self.createInstance(class_name, parameters[3:]) if not new_instance: raise ParameterException("Creating instance: no such class: " + class_name) #index = association.addInstance(new_instance) try: index = association.addInstance(new_instance) except AssociationException as exception: raise RuntimeException( "Error adding instance to association '" + association_name + "': " + str(exception)) p = new_instance.associations.get("parent") if p: p.addInstance(source) source.addEvent( Event("instance_created", None, [association_name + "[" + str(index) + "]"])) else: source.addEvent( Event("instance_creation_error", None, [association_name])) def handleDeleteInstanceEvent(self, parameters): if len(parameters) < 2: raise ParameterException( "The delete event needs at least 2 parameters.") else: source = parameters[0] association_name = parameters[1] traversal_list = self.processAssociationReference(association_name) instances = self.getInstances(source, traversal_list) #association = self.instances_map[source].getAssociation(traversal_list[0][0]) association = source.associations[traversal_list[0][0]] for i in instances: try: association.removeInstance(i["instance"]) except AssociationException as exception: raise RuntimeException( "Error removing instance from association '" + association_name + "': " + str(exception)) i["instance"].stop() #if hasattr(i.instance, 'user_defined_destructor'): i["instance"].user_defined_destructor() source.addEvent( Event("instance_deleted", parameters=[parameters[1]])) def handleAssociateEvent(self, parameters): if len(parameters) != 3: raise ParameterException( "The associate_instance event needs 3 parameters.") else: source = parameters[0] to_copy_list = self.getInstances( source, self.processAssociationReference(parameters[1])) if len(to_copy_list) != 1: raise AssociationReferenceException( "Invalid source association reference.") wrapped_to_copy_instance = to_copy_list[0]["instance"] dest_list = self.processAssociationReference(parameters[2]) if len(dest_list) == 0: raise AssociationReferenceException( "Invalid destination association reference.") last = dest_list.pop() if last[1] != -1: raise AssociationReferenceException( "Last association name in association reference should not be accompanied by an index." ) for i in self.getInstances(source, dest_list): i["instance"].associations[last[0]].addInstance( wrapped_to_copy_instance) def handleNarrowCastEvent(self, parameters): if len(parameters) != 3: raise ParameterException( "The associate_instance event needs 3 parameters.") source = parameters[0] traversal_list = self.processAssociationReference(parameters[1]) cast_event = parameters[2] for i in self.getInstances(source, traversal_list): i["instance"].addEvent(cast_event) def getInstances(self, source, traversal_list): currents = [{ "instance": source, "ref": None, "assoc_name": None, "assoc_index": None }] #currents = [source] for (name, index) in traversal_list: nexts = [] for current in currents: association = current["instance"].associations[name] if (index >= 0): nexts.append({ "instance": association.instances[index], "ref": current["instance"], "assoc_name": name, "assoc_index": index }) elif (index == -1): for i in association.instances: nexts.append({ "instance": association.instances[i], "ref": current["instance"], "assoc_name": name, "assoc_index": index }) #nexts.extend( association.instances.values() ) else: raise AssociationReferenceException( "Incorrect index in association reference.") currents = nexts return currents @abc.abstractmethod def instantiate(self, class_name, construct_params): pass def createInstance(self, to_class, construct_params=[]): instance = self.instantiate(to_class, construct_params) self.instances.append(instance) return instance
class ObjectManagerBase(object): __metaclass__ = abc.ABCMeta def __init__(self, controller): self.controller = controller self.events = EventQueue() self.instances = [] #a dictionary that maps RuntimeClassBase to InstanceWrapper def addEvent(self, event, time_offset = 0.0): self.events.add(event, time_offset) # Broadcast an event to all instances def broadcast(self, new_event): for i in self.instances: i.addEvent(new_event) def getWaitTime(self): #first get waiting time of the object manager's events smallest_time = self.events.getEarliestTime() #check all the instances for instance in self.instances: smallest_time = min(smallest_time, instance.getEarliestEventTime()) return smallest_time def stepAll(self, delta): self.step(delta) for i in self.instances: i.step(delta) def step(self, delta): self.events.decreaseTime(delta) for event in self.events.popDueEvents() : self.handleEvent(event) def start(self): for i in self.instances: i.start() def handleEvent(self, e): if e.getName() == "narrow_cast" : self.handleNarrowCastEvent(e.getParameters()) elif e.getName() == "broad_cast" : self.handleBroadCastEvent(e.getParameters()) elif e.getName() == "create_instance" : self.handleCreateEvent(e.getParameters()) elif e.getName() == "associate_instance" : self.handleAssociateEvent(e.getParameters()) elif e.getName() == "start_instance" : self.handleStartInstanceEvent(e.getParameters()) elif e.getName() == "delete_instance" : self.handleDeleteInstanceEvent(e.getParameters()) def processAssociationReference(self, input_string): if len(input_string) == 0 : raise AssociationReferenceException("Empty association reference.") regex_pattern = re.compile("^([a-zA-Z_]\w*)(?:\[(\d+)\])?$") path_string = input_string.split("/") result = [] for piece in path_string : match = regex_pattern.match(piece) if match : name = match.group(1) index = match.group(2) if index is None : index = -1 result.append((name,int(index))) else : raise AssociationReferenceException("Invalid entry in association reference. Input string: " + input_string) return result def handleStartInstanceEvent(self, parameters): if len(parameters) != 2 : raise ParameterException ("The start instance event needs 2 parameters.") else : source = parameters[0] traversal_list = self.processAssociationReference(parameters[1]) for i in self.getInstances(source, traversal_list) : i["instance"].start() source.addEvent(Event("instance_started", parameters = [parameters[1]])) def handleBroadCastEvent(self, parameters): if len(parameters) != 1 : raise ParameterException ("The broadcast event needs 1 parameter.") self.broadcast(parameters[0]) def handleCreateEvent(self, parameters): if len(parameters) < 2 : raise ParameterException ("The create event needs at least 2 parameters.") source = parameters[0] association_name = parameters[1] association = source.associations[association_name] #association = self.instances_map[source].getAssociation(association_name) if association.allowedToAdd() : ''' allow subclasses to be instantiated ''' class_name = association.to_class if len(parameters) == 2 else parameters[2] new_instance = self.createInstance(class_name, parameters[3:]) if not new_instance: raise ParameterException("Creating instance: no such class: " + class_name) #index = association.addInstance(new_instance) try: index = association.addInstance(new_instance) except AssociationException as exception: raise RuntimeException("Error adding instance to association '" + association_name + "': " + str(exception)) p = new_instance.associations.get("parent") if p: p.addInstance(source) source.addEvent(Event("instance_created", None, [association_name+"["+str(index)+"]"])) else : source.addEvent(Event("instance_creation_error", None, [association_name])) def handleDeleteInstanceEvent(self, parameters): if len(parameters) < 2 : raise ParameterException ("The delete event needs at least 2 parameters.") else : source = parameters[0] association_name = parameters[1] traversal_list = self.processAssociationReference(association_name) instances = self.getInstances(source, traversal_list) #association = self.instances_map[source].getAssociation(traversal_list[0][0]) association = source.associations[traversal_list[0][0]] for i in instances: try: association.removeInstance(i["instance"]) except AssociationException as exception: raise RuntimeException("Error removing instance from association '" + association_name + "': " + str(exception)) i["instance"].stop() #if hasattr(i.instance, 'user_defined_destructor'): i["instance"].user_defined_destructor() source.addEvent(Event("instance_deleted", parameters = [parameters[1]])) def handleAssociateEvent(self, parameters): if len(parameters) != 3 : raise ParameterException ("The associate_instance event needs 3 parameters.") else : source = parameters[0] to_copy_list = self.getInstances(source,self.processAssociationReference(parameters[1])) if len(to_copy_list) != 1 : raise AssociationReferenceException ("Invalid source association reference.") wrapped_to_copy_instance = to_copy_list[0]["instance"] dest_list = self.processAssociationReference(parameters[2]) if len(dest_list) == 0 : raise AssociationReferenceException ("Invalid destination association reference.") last = dest_list.pop() if last[1] != -1 : raise AssociationReferenceException ("Last association name in association reference should not be accompanied by an index.") for i in self.getInstances(source, dest_list) : i["instance"].associations[last[0]].addInstance(wrapped_to_copy_instance) def handleNarrowCastEvent(self, parameters): if len(parameters) != 3 : raise ParameterException ("The associate_instance event needs 3 parameters.") source = parameters[0] traversal_list = self.processAssociationReference(parameters[1]) cast_event = parameters[2] for i in self.getInstances(source, traversal_list) : i["instance"].addEvent(cast_event) def getInstances(self, source, traversal_list): currents = [{ "instance" : source, "ref" : None, "assoc_name" : None, "assoc_index" : None }] #currents = [source] for (name, index) in traversal_list : nexts = [] for current in currents : association = current["instance"].associations[name] if (index >= 0 ) : nexts.append({ "instance" : association.instances[index], "ref" : current["instance"], "assoc_name" : name, "assoc_index" : index }) elif (index == -1) : for i in association.instances: nexts.append({ "instance" : association.instances[i], "ref" : current["instance"], "assoc_name" : name, "assoc_index" : index }) #nexts.extend( association.instances.values() ) else : raise AssociationReferenceException("Incorrect index in association reference.") currents = nexts return currents @abc.abstractmethod def instantiate(self, class_name, construct_params): pass def createInstance(self, to_class, construct_params = []): instance = self.instantiate(to_class, construct_params) self.instances.append(instance) return instance
class RuntimeClassBase(object): __metaclass__ = abc.ABCMeta def __init__(self, controller): self.active = False self.is_stable = True self.events = EventQueue() self.controller = controller self.timers = None self.inports = {} self.semantics = StatechartSemantics() def start(self): self.current_state = {} self.history_state = {} self.timers = {} self.big_step = BigStepState() self.combo_step = ComboStepState() self.small_step = SmallStepState() self.active = True self.is_stable = False self.initializeStatechart() self.processBigStepOutput() def stop(self): self.active = False def addEvent(self, event_list, time_offset = 0.0): if not isinstance(event_list, list): event_list = [event_list] self.events.add(event_list, time_offset) def getEarliestEventTime(self) : if not self.active: return INFINITY if not self.is_stable: return 0.0 if self.timers: return min(self.events.getEarliestTime(), min(self.timers.itervalues())) return self.events.getEarliestTime() def processBigStepOutput(self): for e in self.big_step.getOutputEvents(): self.controller.outputEvent(e) for e in self.big_step.getOutputEventsOM(): self.controller.object_manager.addEvent(e) def step(self, delta): if not self.active : return # decrease event queue time self.events.decreaseTime(delta) # decrease timers time next_timers = {} for (key,value) in self.timers.iteritems() : time = value - delta if time <= 0.0 : self.addEvent( Event("_" + str(key) + "after"), time) else : next_timers[key] = time self.timers = next_timers # execute big step(s) due = self.events.popDueEvents() if not due and not self.is_stable: due = [[]] for input_events in due: # perform 1 big step per slot in 'due' self.is_stable = not self.bigStep(input_events) self.processBigStepOutput() def inState(self, nodes): for c in self.current_state.itervalues(): new_nodes = [] for n in nodes: if not (n in c): new_nodes.append(n) nodes = new_nodes if len(nodes) == 0: return True return False def bigStep(self, input_events): #print "new big step" self.big_step.next(input_events) self.small_step.reset() self.combo_step.reset() while self.comboStep(): self.big_step.setStepped() if self.semantics.big_step_maximality == StatechartSemantics.TakeOne: break # Take One -> only one combo step allowed return self.big_step.hasStepped() def comboStep(self): #print "new combo step" self.combo_step.next() while self.smallStep(): self.combo_step.setStepped() return self.combo_step.hasStepped() def smallStep(self): if self.small_step.hasStepped(): self.small_step.next() self.generateCandidates() if self.small_step.hasCandidates(): #print "new small step, have " + str(len(self.small_step.getCandidates())) + " candidates" if self.semantics.concurrency == StatechartSemantics.Single: transition, parameters = self.small_step.getCandidates()[0] # execute first of candidates transition(parameters) elif self.semantics.concurrency == StatechartSemantics.Many: pass # TODO: implement self.small_step.setStepped() return self.small_step.hasStepped() def getEnabledEvents(self): result = self.small_step.getCurrentEvents() + self.combo_step.getCurrentEvents() if self.semantics.input_event_lifeline == StatechartSemantics.Whole or ( not self.big_step.hasStepped() and (self.semantics.input_event_lifeline == StatechartSemantics.FirstComboStep or ( not self.combo_step.hasStepped() and self.semantics.input_event_lifeline == StatechartSemantics.FirstSmallStep))): result += self.big_step.getInputEvents() return result def raiseInternalEvent(self, event): if self.semantics.internal_event_lifeline == StatechartSemantics.NextSmallStep: self.small_step.addNextEvent(event) elif self.semantics.internal_event_lifeline == StatechartSemantics.NextComboStep: self.combo_step.addNextEvent(event) elif self.semantics.internal_event_lifeline == StatechartSemantics.Queue: self.events.add([event], 0.0) @abc.abstractmethod def initializeStatechart(self): pass @abc.abstractmethod def generateCandidates(self): pass
class ControllerBase(object): def __init__(self, object_manager): self.object_manager = object_manager self.private_port_counter = 0 # Keep track of input ports self.input_ports = {} self.input_queue = EventQueue() # Keep track of output ports self.output_ports = [] self.output_listeners = [] # Let statechart run one last time before stopping self.done = False def addInputPort(self, virtual_name, instance = None): if instance == None : port_name = virtual_name else: port_name = "private_" + str(self.private_port_counter) + "_" + virtual_name self.private_port_counter += 1 self.input_ports[port_name] = InputPortEntry(virtual_name, instance) return port_name def addOutputPort(self, port_name): self.output_ports.append(port_name) def broadcast(self, new_event): self.object_manager.broadcast(new_event) def start(self): self.object_manager.start() def stop(self): pass def addInput(self, input_event_list, time_offset = 0.0): if not isinstance(input_event_list, list): input_event_list = [input_event_list] for e in input_event_list: if e.getName() == "" : raise InputException("Input event can't have an empty name.") if e.getPort() not in self.input_ports : raise InputException("Input port mismatch, no such port: " + e.getPort() + ".") self.input_queue.add(input_event_list, time_offset) def getWaitTime(self): return min(self.object_manager.getWaitTime(), self.input_queue.getEarliestTime()) def handleInput(self, delta): self.input_queue.decreaseTime(delta) for events in self.input_queue.popDueEvents(): for e in events: input_port = self.input_ports[e.getPort()] e.port = input_port.virtual_name target_instance = input_port.instance if target_instance == None: self.broadcast(e) else: target_instance.addEvent(e) def outputEvent(self, event): for listener in self.output_listeners : listener.add(event) def addOutputListener(self, ports): listener = OutputListener(ports) self.output_listeners.append(listener) return listener def addMyOwnOutputListener(self, listener): self.output_listeners.append(listener) # deprecated, to add multiple events, use addInput instead def addEventList(self, event_list): for (event, time_offset) in event_list : self.addInput(event, time_offset) def getObjectManager(self): return self.object_manager