Przeglądaj źródła

added global output ports but needs checking + further need to optimize refs

sampieters 1 rok temu
rodzic
commit
7b985aec3d

+ 2 - 2
examples/BouncingBalls/PyDEVS/runner.py

@@ -1,5 +1,5 @@
 import tkinter as tk
-import target as target
+import the_target as target
 from sccd.runtime.libs.ui_v2 import UI
 from sccd.runtime.DEVS_loop import DEVSSimulator
 
@@ -15,7 +15,7 @@ class OutputListener:
 
 if __name__ == '__main__':
 	model = target.Controller(name="controller")
-	refs = {"ui": model.in_ui, "field_ui": model.atomic1.field_ui, "button_ui": model.atomic2.button_ui, "ball_ui": model.atomic3.ball_ui}
+	refs = {"ui": model.in_ui, "field_ui": model.atomics[1].field_ui, "button_ui": model.atomics[2].button_ui, "ball_ui": model.atomics[3].ball_ui}
 
 	tkroot = tk.Tk()
 	tkroot.withdraw()

+ 75 - 12
examples/BouncingBalls/PyDEVS/the_target.py

@@ -34,6 +34,9 @@ class MainAppInstance(RuntimeClassBase):
         MainAppInstance.user_defined_constructor(self)
         port_name = Ports.addInputPort("<narrow_cast>", self)
         atomdevs.addInPort(port_name)
+
+        # TODO: This still needs to be added to the DEVS compiler
+        #atomdevs.state.port_mappings["ui"] = None  
     
     def user_defined_constructor(self):
         self.nr_of_fields = 0
@@ -233,6 +236,10 @@ class FieldInstance(RuntimeClassBase):
         FieldInstance.user_defined_constructor(self)
         port_name = Ports.addInputPort("<narrow_cast>", self)
         atomdevs.addInPort(port_name)
+
+        # TODO: This still needs to be added to the DEVS compiler
+        #atomdevs.state.port_mappings["ui"] = None        
+
         port_name = Ports.addInputPort("field_ui", self)
         atomdevs.addInPort(port_name)
         atomdevs.state.port_mappings[port_name] = atomdevs.state.next_instance
@@ -506,6 +513,10 @@ class ButtonInstance(RuntimeClassBase):
         ButtonInstance.user_defined_constructor(self, window_id, event_name, button_text)
         port_name = Ports.addInputPort("<narrow_cast>", self)
         atomdevs.addInPort(port_name)
+
+        # TODO: This still needs to be added to the DEVS compiler
+        #atomdevs.state.port_mappings["ui"] = None  
+
         port_name = Ports.addInputPort("button_ui", self)
         atomdevs.addInPort(port_name)
         atomdevs.state.port_mappings[port_name] = atomdevs.state.next_instance
@@ -611,6 +622,11 @@ class BallInstance(RuntimeClassBase):
         BallInstance.user_defined_constructor(self, canvas_id, x, y)
         port_name = Ports.addInputPort("<narrow_cast>", self)
         atomdevs.addInPort(port_name)
+
+
+        # TODO: This still needs to be added to the DEVS compiler
+        #atomdevs.state.port_mappings["ui"] = None  
+
         port_name = Ports.addInputPort("ball_ui", self)
         atomdevs.addInPort(port_name)
         atomdevs.state.port_mappings[port_name] = atomdevs.state.next_instance
@@ -835,25 +851,72 @@ class Controller(CoupledDEVS):
         self.out_ui = self.addOutPort("ui")
         Ports.addOutputPort("ui")
         self.objectmanager = self.addSubModel(ObjectManager("ObjectManager"))
-        self.atomic0 = self.addSubModel(MainApp("MainApp"))
-        self.atomic1 = self.addSubModel(Field("Field"))
-        self.atomic2 = self.addSubModel(Button("Button"))
-        self.atomic3 = self.addSubModel(Ball("Ball"))
-        self.connectPorts(self.atomic0.obj_manager_out, self.objectmanager.input)
-        self.connectPorts(self.objectmanager.output["MainApp"], self.atomic0.obj_manager_in)
+        #self.atomic0 = self.addSubModel(MainApp("MainApp"))
+        #self.atomic1 = self.addSubModel(Field("Field"))
+        #self.atomic2 = self.addSubModel(Button("Button"))
+        #self.atomic3 = self.addSubModel(Ball("Ball"))
+
+        self.atomics = [
+            self.addSubModel(MainApp("MainApp")),
+            self.addSubModel(Field("Field")),
+            self.addSubModel(Button("Button")),
+            self.addSubModel(Ball("Ball"))
+        ]
+
+        for atomic in self.atomics:
+            # Connect to object manager
+            self.connectPorts(atomic.obj_manager_out, self.objectmanager.input)
+            self.connectPorts(self.objectmanager.output[atomic.name], atomic.obj_manager_in)
+
+            # TODO: check if this is correct
+            # Connect global input ports
+            for global_in in self.IPorts:
+                self.connectPorts(global_in, atomic.input)
+            
+            # Connect global output ports
+            for global_out in self.OPorts:
+                self.connectPorts(atomic.output, global_out)
+
+
+            
+        self.connectPorts(self.atomics[0].outputs["fields"], self.atomics[1].input)
+        self.connectPorts(self.atomics[1].outputs["balls"], self.atomics[3].input)
+        self.connectPorts(self.atomics[1].outputs["buttons"], self.atomics[2].input)
+        self.connectPorts(self.atomics[1].outputs["parent"], self.atomics[0].input)
+        self.connectPorts(self.atomics[2].outputs["parent"], self.atomics[1].input)
+        self.connectPorts(self.atomics[3].outputs["parent"], self.atomics[1].input)
+        #self.connectPorts(self.atomics[0].output, self.out_ui)
+        #self.connectPorts(self.atomics[1].output, self.out_ui)
+        #self.connectPorts(self.atomics[2].output, self.out_ui)
+        #self.connectPorts(self.atomics[3].output, self.out_ui)
+
+        # TODO
+        #self.connectPorts(self.in_ui, self.atomics[0].input)
+        #self.connectPorts(self.in_ui, self.atomics[1].input)
+        #self.connectPorts(self.in_ui, self.atomics[2].input)
+        #self.connectPorts(self.in_ui, self.atomics[3].input)
+
+
+
+
+        
+        '''
+        #self.connectPorts(self.atomic0.obj_manager_out, self.objectmanager.input)
+        #self.connectPorts(self.objectmanager.output["MainApp"], self.atomic0.obj_manager_in)
         self.connectPorts(self.atomic0.outputs["fields"], self.atomic1.input)
         self.connectPorts(self.atomic1.obj_manager_out, self.objectmanager.input)
-        self.connectPorts(self.objectmanager.output["Field"], self.atomic1.obj_manager_in)
+        #self.connectPorts(self.objectmanager.output["Field"], self.atomic1.obj_manager_in)
         self.connectPorts(self.atomic1.outputs["balls"], self.atomic3.input)
         self.connectPorts(self.atomic1.outputs["buttons"], self.atomic2.input)
         self.connectPorts(self.atomic1.outputs["parent"], self.atomic0.input)
-        self.connectPorts(self.atomic2.obj_manager_out, self.objectmanager.input)
-        self.connectPorts(self.objectmanager.output["Button"], self.atomic2.obj_manager_in)
+        #self.connectPorts(self.atomic2.obj_manager_out, self.objectmanager.input)
+        #self.connectPorts(self.objectmanager.output["Button"], self.atomic2.obj_manager_in)
         self.connectPorts(self.atomic2.outputs["parent"], self.atomic1.input)
-        self.connectPorts(self.atomic3.obj_manager_out, self.objectmanager.input)
-        self.connectPorts(self.objectmanager.output["Ball"], self.atomic3.obj_manager_in)
+        #self.connectPorts(self.atomic3.obj_manager_out, self.objectmanager.input)
+        #self.connectPorts(self.objectmanager.output["Ball"], self.atomic3.obj_manager_in)
         self.connectPorts(self.atomic3.outputs["parent"], self.atomic1.input)
         self.connectPorts(self.atomic0.output, self.out_ui)
         self.connectPorts(self.atomic1.output, self.out_ui)
         self.connectPorts(self.atomic2.output, self.out_ui)
-        self.connectPorts(self.atomic3.output, self.out_ui)
+        self.connectPorts(self.atomic3.output, self.out_ui)
+        '''

Plik diff jest za duży
+ 0 - 12901
examples/BouncingBalls/Python/trace.txt


+ 15 - 3
pypdevs/basesimulator.py

@@ -1125,12 +1125,24 @@ class BaseSimulator(Solver):
                 # We are outputting on a Coupled DEVS model, so we just find out what its ports are connected to
                 time_diff = time.time() - self.rt_zerotime
                 tn = time_diff / self.realtime_scale
-                for p, z in event_port.routing_outline:
-                    ev = event_value if z is None else z(event_value)
-                    msg = {p: [ev]}
+                
+                # TODO: SAM changed this
+                for p in event_port.outline:
+                    ev = event_value
+                    msg = {p : [ev]}
                     p.host_DEVS.my_input = msg
                     self.transitioning[p.host_DEVS] = 2
                     self.model.time_next = (tn, 1)
+
+
+                #for p, z in event_port.routing_outline:
+                #    ev = event_value if z is None else z(event_value)
+                #    msg = {p: [ev]}
+                #    p.host_DEVS.my_input = msg
+                #    self.transitioning[p.host_DEVS] = 2
+                #    self.model.time_next = (tn, 1)
+
+
             # Transition
             return False
 

+ 3 - 6
sccd/compiler/DEVS_generator.py

@@ -260,7 +260,7 @@ class DEVSGenerator(Visitor):
             #self.writer.addAssignment(GLC.SelfProperty(f"inports[\"{inp}\"]"), f"(\'{inp}\', atomdevs.next_instance)")
             self.writer.addAssignment("port_name", GLC.FunctionCall("Ports.addInputPort", [GLC.String(inp), "self"]))
             self.writer.add(GLC.FunctionCall("atomdevs.addInPort", ["port_name"]))
-            self.writer.addAssignment("atomdevs.port_mappings[port_name]", "atomdevs.next_instance")
+            self.writer.addAssignment("atomdevs.state.port_mappings[port_name]", "atomdevs.state.next_instance")
             self.writer.addAssignment(f"self.inports[\"{inp}\"]", "port_name")
 
 
@@ -393,11 +393,8 @@ class DEVSGenerator(Visitor):
                 GLC.FunctionCall(GLC.Property("controller", "addOutputPort"), [GLC.String(p), GLC.SelfExpression()]))
 
         if constructor.parent_class.name == constructor.parent_class.class_diagram.default_class.name:
-            #self.writer.add(GLC.FunctionCall(GLC.SelfProperty("instances.append"), [f"{constructor.parent_class.name}Instance(self)"]))
-            self.writer.addAssignment("self.instances[self.next_instance]", f"{constructor.parent_class.name}Instance(self)")
-            self.writer.addAssignment("self.next_instance", "self.next_instance + 1")
-            #self.writer.addAssignment("port_name", GLC.FunctionCall("Ports.addInputPort", [GLC.String("<narrow_cast>"), "0"]))
-            #self.writer.add(GLC.FunctionCall("self.addInPort", ["port_name"]))
+            self.writer.addAssignment("self.state.instances[self.state.next_instance]", f"{constructor.parent_class.name}Instance(self)")
+            self.writer.addAssignment("self.state.next_instance", "self.state.next_instance + 1")
 
         self.writer.endMethodBody()
         self.writer.endConstructor()

+ 17 - 0
sccd/runtime/DEVS_loop.py

@@ -14,6 +14,20 @@ def get_port(text):
 class DEVSSimulator(Simulator):
 	def __init__(self, model, inputs={}):
 		super().__init__(model)
+
+		# TODO: Add the input ports here so it works without manually adding them
+		inputs ={}
+
+		# Add global inports
+		for global_in in model.IPorts:
+			inputs[global_in.name] = global_in
+
+		# Add private ports (can't send to it unless it knows the id)
+		for aclass in model.atomics:
+			pass
+
+
+
 		self.setRealTimePorts(inputs)	
 	
 	def addInput(self, event):
@@ -21,4 +35,7 @@ class DEVSSimulator(Simulator):
 		event_string = f"Event(\"{event.name}\",\"{event.port}\",{event.parameters})"
 		event_string = event_string.replace(" ", "")
 		interrupt_string = f"{port_name} {event_string}"
+
+		#event_string = f"Event(\"{event.name}\",\"{"ui"}\",{event.parameters})"
+		#interrupt_string = f"ui {event_string}"
 		self.realtime_interrupt(interrupt_string)

+ 37 - 343
sccd/runtime/DEVS_statecharts_core.py

@@ -4,6 +4,8 @@ from sccd.runtime.event_queue import EventQueue
 from pypdevs.infinity import INFINITY
 from pypdevs.DEVS import *
 
+from sccd.runtime.statecharts_core import StatechartSemantics, State, HistoryState, ShallowHistoryState, DeepHistoryState, ParallelState, Transition, BigStepState, ComboStepState, SmallStepState, RuntimeException, AssociationException, Association
+
 from heapq import heappush, heappop, heapify
 import threading
 
@@ -15,210 +17,7 @@ def get_private_port(text):
     if match:
         result = match.group(1)
         return result
-
-class RuntimeException(Exception):
-    """
-    Base class for runtime exceptions.
-    """
-    def __init__(self, message):
-        self.message = message
-    def __str__(self):
-        return repr(self.message)
-
-
-class AssociationException(RuntimeException):
-    """
-    Exception class thrown when an error occurs in a CRUD operation on associations.
-    """
-    pass
-
-
-class Association(object):
-    """
-    Class representing an SCCD association.
-    """
-
-    def __init__(self, to_class, min_card, max_card):
-        """
-        Constructor
-
-       :param to_class: the name of the target class
-       :param min_card: the minimal cardinality
-       :param max_card: the maximal cardinality
-        """
-        self.to_class = to_class
-        self.min_card = min_card
-        self.max_card = max_card
-        self.instances = {}  # maps index (as string) to instance
-        self.instances_to_ids = {}
-        self.size = 0
-        self.next_id = 0
-
-    def allowedToAdd(self):
-        return self.max_card == -1 or self.size < self.max_card
-
-    def allowedToRemove(self):
-        return self.min_card == -1 or self.size > self.min_card
-
-    def addInstance(self, instance):
-        if self.allowedToAdd():
-            new_id = self.next_id
-            self.next_id += 1
-            self.instances[new_id] = instance
-            self.instances_to_ids[instance] = new_id
-            self.size += 1
-            return new_id
-        else:
-            raise AssociationException("Not allowed to add the instance to the association.")
-
-    def removeInstance(self, instance):
-        if self.allowedToRemove():
-            index = self.instances_to_ids[instance]
-            del self.instances[index]
-            del self.instances_to_ids[instance]
-            #self.size -= 1
-            return index
-        else:
-            raise AssociationException("Not allowed to remove the instance from the association.")
-
-    def getInstance(self, index):
-        try:
-            return self.instances[index]
-        except IndexError:
-            raise AssociationException("Invalid index for fetching instance(s) from association.")
-
-
-class StatechartSemantics:
-    # Big Step Maximality
-    TakeOne = 0
-    TakeMany = 1
-    # Concurrency - not implemented yet
-    Single = 0
-    Many = 1
-    # Preemption - not implemented yet
-    NonPreemptive = 0
-    Preemptive = 1
-    # Internal Event Lifeline
-    Queue = 0
-    NextSmallStep = 1
-    NextComboStep = 2
-    # Input Event Lifeline
-    Whole = 0
-    FirstSmallStep = 1
-    FirstComboStep = 2
-    # Priority
-    SourceParent = 0
-    SourceChild = 1
-
-    # TODO: add Memory Protocol options
-
-    def __init__(self):
-        # default semantics:
-        self.big_step_maximality = self.TakeMany
-        self.internal_event_lifeline = self.Queue
-        self.input_event_lifeline = self.FirstComboStep
-        self.priority = self.SourceParent
-        self.concurrency = self.Single
-
-
-class State:
-    def __init__(self, state_id, name, obj):
-        self.state_id = state_id
-        self.name = name
-        self.obj = obj
-
-        self.ancestors = []
-        self.descendants = []
-        self.descendant_bitmap = 0
-        self.children = []
-        self.parent = None
-        self.enter = None
-        self.exit = None
-        self.default_state = None
-        self.transitions = []
-        self.history = []
-        self.has_eventless_transitions = False
-
-    def getEffectiveTargetStates(self):
-        targets = [self]
-        if self.default_state:
-            targets.extend(self.default_state.getEffectiveTargetStates())
-        return targets
-
-    def fixTree(self):
-        for c in self.children:
-            if isinstance(c, HistoryState):
-                self.history.append(c)
-            c.parent = self
-            c.ancestors.append(self)
-            c.ancestors.extend(self.ancestors)
-            c.fixTree()
-        self.descendants.extend(self.children)
-        for c in self.children:
-            self.descendants.extend(c.descendants)
-        for d in self.descendants:
-            self.descendant_bitmap |= 2 ** d.state_id
-
-    def addChild(self, child):
-        self.children.append(child)
-
-    def addTransition(self, transition):
-        self.transitions.append(transition)
-
-    def setEnter(self, enter):
-        self.enter = enter
-
-    def setExit(self, exit):
-        self.exit = exit
-
-    def __repr__(self):
-        return "State(%s)" % (self.state_id)
-
-
-class HistoryState(State):
-    def __init__(self, state_id, name, obj):
-        State.__init__(self, state_id, name, obj)
-
-
-class ShallowHistoryState(HistoryState):
-    def __init__(self, state_id, name, obj):
-        HistoryState.__init__(self, state_id, name, obj)
-
-    def getEffectiveTargetStates(self):
-        if self.state_id in self.obj.history_values:
-            targets = []
-            for hv in self.obj.history_values[self.state_id]:
-                targets.extend(hv.getEffectiveTargetStates())
-            return targets
-        else:
-            # TODO: is it correct that in this case, the parent itself is also entered?
-            return self.parent.getEffectiveTargetStates()
-
-
-class DeepHistoryState(HistoryState):
-    def __init__(self, state_id, name, obj):
-        HistoryState.__init__(self, state_id, name, obj)
-
-    def getEffectiveTargetStates(self):
-        if self.state_id in self.obj.history_values:
-            return self.obj.history_values[self.state_id]
-        else:
-            # TODO: is it correct that in this case, the parent itself is also entered?
-            return self.parent.getEffectiveTargetStates()
-
-
-class ParallelState(State):
-    def __init__(self, state_id, name, obj):
-        State.__init__(self, state_id, name, obj)
-
-    def getEffectiveTargetStates(self):
-        targets = [self]
-        for c in self.children:
-            if not isinstance(c, HistoryState):
-                targets.extend(c.getEffectiveTargetStates())
-        return targets
-
-
+    
 class Transition:
     def __init__(self, obj, source, targets):
         self.guard = None
@@ -371,7 +170,6 @@ class Event(object):
 
 
 class RuntimeClassBase(object):
-
     def __init__(self, controller):
         self.events = EventQueue()
 
@@ -390,13 +188,9 @@ class RuntimeClassBase(object):
         self.transition_mem = {}
         self.config_mem = {}
 
-
-        #self.narrow_cast_port = self.controller.addInputPort("<narrow_cast>", self)
-
         self.semantics = StatechartSemantics()
 
-    # to break ties in the heap,
-    # compare by number of events in the list
+    # to break ties in the heap, compare by number of events in the list
     def __lt__(self, other):
         return len(self.events.event_list) < len(other.events.event_list)
 
@@ -440,11 +234,9 @@ class RuntimeClassBase(object):
         self.configuration_bitmap = sum([2 ** s.state_id for s in states])
 
     def getSimulatedTime(self):
-        # TODO: added to be exactly the same as sccd
         return self.controller.state.simulated_time * 1000
 
     def addTimer(self, index, timeout):
-        #self.timers_to_add[index] = (self.controller.simulated_time + int(timeout * 1000), Event("_%iafter" % index))
         self.timers_to_add[index] = (self.controller.state.simulated_time + timeout, Event("_%iafter" % index))
 
     def removeTimer(self, index):
@@ -470,7 +262,6 @@ class RuntimeClassBase(object):
         for e in self.big_step.output_events_port:
             self.controller.state.outputEvent(e)
         for e in self.big_step.output_events_om:
-            #TODO: is this the same as obbject manager, so cd scope?
             self.controller.state.addEvent(e)
 
     def __set_stable(self, is_stable):
@@ -614,73 +405,6 @@ class RuntimeClassBase(object):
             pass
             # TODO: Check (untill now no problems)
             #self.controller.object_manager.eventless.add(self)
-
-class BigStepState(object):
-    def __init__(self):
-        self.input_events = [] # input events received from environment before beginning of big step (e.g. from object manager, from input port)
-        self.output_events_port = [] # output events to be sent to output port after big step ends.
-        self.output_events_om = [] # output events to be sent to object manager after big step ends.
-        self.has_stepped = True
-
-    def next(self, input_events):
-        self.input_events = input_events
-        self.output_events_port = []
-        self.output_events_om = []
-        self.has_stepped = False
-
-    def outputEvent(self, event):
-        self.output_events_port.append(event)
-
-    def outputEventOM(self, event):
-        self.output_events_om.append(event)
-
-
-class ComboStepState(object):
-    def __init__(self):
-        self.current_events = [] # set of enabled events during combo step
-        self.next_events = [] # internal events that were raised during combo step
-        self.changed_bitmap = 0 # set of all or-states that were the arena of a triggered transition during big step.
-        self.has_stepped = True
-
-    def reset(self):
-        self.current_events = []
-        self.next_events = []
-
-    def next(self):
-        self.current_events = self.next_events
-        self.next_events = []
-        self.changed_bitmap = 0
-        self.has_stepped = False
-
-    def addNextEvent(self, event):
-        self.next_events.append(event)
-
-
-class SmallStepState(object):
-    def __init__(self):
-        self.current_events = [] # set of enabled events during small step
-        self.next_events = [] # events to become 'current' in the next small step
-        self.candidates = [] # document-ordered(!) list of transitions that can potentially be executed concurrently, or preempt each other, depending on concurrency semantics. If no concurrency is used and there are multiple candidates, the first one is chosen. Source states of candidates are *always* orthogonal to each other.
-        self.has_stepped = True
-
-    def reset(self):
-        self.current_events = []
-        self.next_events = []
-
-    def next(self):
-        self.current_events = self.next_events # raised events from previous small step
-        self.next_events = []
-        self.candidates = []
-        self.has_stepped = False
-
-    def addNextEvent(self, event):
-        self.next_events.append(event)
-
-    def addCandidate(self, t, p):
-        self.candidates.append((t, p))
-
-    def hasCandidates(self):
-        return len(self.candidates) > 0
     
 class ClassState():
     def __init__(self, name) -> None:
@@ -758,30 +482,20 @@ class ClassState():
     def handleInput(self):
         while not self.input_queue.isEmpty():
             event_time = self.input_queue.getEarliestTime()
-            e = self.input_queue.pop()
+            event = self.input_queue.pop()
             
-            #TODO: tot nu toe zal dit werken maar niet 
-            #input_port = self.input_ports[e.getPort()]
-            input_port = e.getPort()
-
-            #target_instance = input_port.instance
-            #TODO: get the first field, should be dynamically
-            #temp = None
-
-            # TODO: instance is now given in parameters
-            temp = e.instance
-            if temp is None:
-                temp = self.processAssociationReference(e.parameters[0])[0]
-                temp = temp[1]
-            #target_instance = list(self.instances)[temp]
-            target_instance = self.instances[temp]
+            #if event.instance is None:
+            #    event.instance = self.processAssociationReference(event.parameters[0])[0][1]
+            if event.instance is None:
+                target_instance = None
+            else:
+                target_instance = self.instances[event.instance]
             if target_instance == None:
-                self.broadcast(e, event_time - self.simulated_time)
+                self.broadcast(None, event, event_time - self.simulated_time)
             else:
-                target_instance.addEvent(e, event_time - self.simulated_time)
+                target_instance.addEvent(event, event_time - self.simulated_time)
 
-    def addInput(self, input_event_list, time_offset = 0, force_internal=False):
-        # force_internal is for narrow_cast events, otherwise these would arrive as external events (on the current wall-clock time)
+    def addInput(self, input_event_list, time_offset = 0):
         if not isinstance(input_event_list, list):
             input_event_list = [input_event_list]
 
@@ -792,12 +506,7 @@ class ClassState():
             #if e.getPort() not in self.IPorts:
             #    raise InputException("Input port mismatch, no such port: " + e.getPort() + ".")
                 
-            if force_internal:
-                self.input_queue.add((0 if self.simulated_time is None else self.simulated_time) + time_offset, e)
-            else:
-                # TODO; changed this from self.accurate_time.get_wct() to self.simulated_time
-                #self.input_queue.add((0 if self.simulated_time is None else 0) + time_offset, e)
-                self.input_queue.add((0 if self.simulated_time is None else 0) + time_offset, e)
+            self.input_queue.add((0 if self.simulated_time is None else 0) + time_offset, e)
 
 
     def handleEvent(self, e):
@@ -1012,17 +721,6 @@ class ClassState():
                     raise AssociationReferenceException("Incorrect index in association reference.")
             currents = nexts
         return currents
-    
-    def instantiate(self, class_name, construct_params):
-        pass
-    
-    def createInstance(self, to_class, construct_params = []):
-        instance = self.instantiate(to_class, construct_params)
-        self.instances.add(instance)
-        return instance
-
-    def addMyOwnOutputListener(self, listener):
-        self.output_listeners.append(listener)
 
 class ClassBase(AtomicDEVS):    
     def __init__(self, name):
@@ -1030,7 +728,7 @@ class ClassBase(AtomicDEVS):
         
         self.outputs = {}
         self.state = ClassState(name)
-        self.elapsed = 0
+        #self.elapsed = 0
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.obj_manager_out = self.addOutPort("obj_manager_out")
 
@@ -1038,16 +736,23 @@ class ClassBase(AtomicDEVS):
         raise "Something went wrong "
 
     def extTransition(self, inputs):
+        # Update simulated time
         self.state.simulated_time += self.elapsed
         self.state.next_time = 0
-        all_inputs = []
-        for input_list in inputs.values():
-            all_inputs.extend(input_list)
+
+        # Collect all inputs
+        all_inputs = [input for input_list in inputs.values() for input in input_list]
         for input in all_inputs:
             if isinstance(input, str):
                 tem = eval(input)
-                tem.instance = self.state.port_mappings[tem.port]
-                tem.port = get_private_port(tem.port)
+
+                # TODO: This works, the instance does not create a ball in bouncing balls but this is probably normal (port != input_port)
+
+                #tem.instance = self.state.port_mappings[tem.port]
+                tem.instance = self.state.port_mappings.setdefault(tem.port, None)
+
+                if tem.instance != None:
+                    tem.port = get_private_port(tem.port)
                 self.state.addInput(tem)
             elif input[2].name == "create_instance":
                 new_instance = self.constructObject(input[2].parameters)
@@ -1119,36 +824,26 @@ class ClassBase(AtomicDEVS):
         return self.state
     
     def intTransition(self):
+        # Update simulated time and clear previous messages 
         self.state.simulated_time += self.state.next_time
-        self.state.next_time = min(self.state.getEarliestEventTime(), self.state.simulated_time + self.state.input_queue.getEarliestTime())
-
         self.state.to_send = []
-        self.state.handleInput()
-        self.state.stepAll()
-
+        # Calculate the next event time, clamp to ensure non-negative result
+        self.state.next_time = min(self.state.getEarliestEventTime(), self.state.simulated_time + self.state.input_queue.getEarliestTime())
         self.state.next_time -= self.state.simulated_time
-
-        # Clamp to ensure non-negative result
         self.state.next_time = max(self.state.next_time, 0.0)
+        # Handle incoming inputs and do a step in all statecharts
+        self.state.handleInput()
+        self.state.stepAll()
         return self.state
 
     def outputFnc(self):
         to_dict = {}
         for sending in self.state.to_send:
             if isinstance(sending, tuple) and sending[2].port == None:
-                if self.obj_manager_out in to_dict:
-                    to_dict[self.obj_manager_out].append(sending)
-                else:
-                    to_dict[self.obj_manager_out] = [sending]
+                to_dict.setdefault(self.obj_manager_out, []).append(sending)
             else:
-                the_port = None
-                for port in self.OPorts:
-                    if port.name == sending.port:
-                        the_port = port
-                if the_port in to_dict:
-                    to_dict[the_port].append(sending)
-                else:
-                    to_dict[the_port] = [sending]
+                the_port = next((port for port in self.OPorts if port.name == sending.port), None)
+                to_dict.setdefault(the_port, []).append(sending)
         return to_dict
     
     def timeAdvance(self):
@@ -1203,4 +898,3 @@ class Ports:
             self.inports[port_name] = instance
             self.private_port_counter += 1
         return port_name
-