Browse Source

use case with adaptations and statecharts that more resemble the ones used in the state of the art.

Cláudio Gomes 8 years ago
parent
commit
7b181de346

+ 19 - 2
SemanticAdaptationForFMI/FMIAbstraction/src/abstract_units/StatechartSimulationUnit_CTInOut.py

@@ -105,6 +105,18 @@ class StatechartSimulationUnit_CTInOut(AbstractSimulationUnit):
         
         self.setValues(0, 0, {self.__current_state: self.__initial_state})
         
+        # Compute all default transitions at this point
+        state_snapshot = self.getValues(0, 0, [self.__current_state])
+        input_snapshot = {}
+        previous_input_snaptshop =  {}
+        
+        (next_state, output_assignments) = self._doStepFunction(0.0, 
+                                                state_snapshot, input_snapshot, previous_input_snaptshop)
+        
+        # Commit the new state and outputs.
+        AbstractSimulationUnit.setValues(self, 0, 0, {self.__current_state : next_state})
+        AbstractSimulationUnit.setValues(self, 0, 0, output_assignments)
+        
         AbstractSimulationUnit.enterInitMode(self)
         
         l.debug("<%s.StatechartSimulationUnit_CTInOut.enterInitMode()", self._name)
@@ -118,8 +130,10 @@ class StatechartSimulationUnit_CTInOut(AbstractSimulationUnit):
             assert step == 0
             state_snapshot = self.getValues(step, iteration, [self.__current_state])
             input_snapshot = self.getValues(step, iteration, self._getInputVars())
+            previous_input_snaptshop =  {}
             
-            (next_state, output_assignments) = self._doStepFunction(0.0, state_snapshot, input_snapshot)
+            (next_state, output_assignments) = self._doStepFunction(0.0, 
+                                                    state_snapshot, input_snapshot, previous_input_snaptshop)
             
             # Commit the new state and outputs.
             AbstractSimulationUnit.setValues(self, step, iteration, {self.__current_state : next_state})
@@ -137,6 +151,8 @@ class StatechartSimulationUnit_CTInOut(AbstractSimulationUnit):
         
         l.debug(">%s._runAllEnabledTransitions(%f, %s, %s)", self._name, time, state, inputs)
         
+        inputs_available = len(inputs) > 0
+        
         transition_taken = True
         statechart_state = state[self.__current_state]
         output_assignments = {}
@@ -148,7 +164,7 @@ class StatechartSimulationUnit_CTInOut(AbstractSimulationUnit):
             It will be merged with output_assignments
             """
             (out_values, new_state, transition_taken, trigger) = \
-                self._state_transition_function(statechart_state, inputs, previous_inputs, elapsed)
+                self._state_transition_function(statechart_state, inputs, previous_inputs, elapsed, inputs_available)
             if transition_taken:
                 l.debug("Transition taken from %s to %s because of %s. Produced assignments: %s.", 
                                 statechart_state, new_state, trigger, out_values)
@@ -157,6 +173,7 @@ class StatechartSimulationUnit_CTInOut(AbstractSimulationUnit):
                 l.debug("Merged assignments: %s", output_assignments)
                 statechart_state = new_state
                 self.__last_transition_time = time
+            
         l.debug("Finished all enabled transitions.")
         
         l.debug("%s.state=%s", self._name, statechart_state)

+ 0 - 2
SemanticAdaptationForFMI/FMIAbstraction/src/abstract_units/StatechartSimulationUnit_Event.py

@@ -114,8 +114,6 @@ class StatechartSimulationUnit_Event(AbstractSimulationUnit):
                 self.__last_transition_time = time
                 if trigger==StatechartSimulationUnit_Event.TRIGGER_INPUT:
                     inputToConsume = ""
-            else:
-                l.debug("No transition taken.")
         l.debug("Finished all enabled transitions.")
         
         l.debug("%s.state=%s", self._name, old_state)

+ 223 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/case_study/scenarios/ControlledScenario_CTStatecharts.py

@@ -0,0 +1,223 @@
+"""
+In this scenario, the controller is a statchart that receives events at his input.
+The main semantic adaptation is getting the continuous armature signal coming from the power system,
+and converting it into an event.
+"""
+
+import logging
+
+from bokeh.plotting import figure, output_file, show
+
+from case_study.units.adaptations.InacurateControllerArmatureAdaptation_CT import InacurateControllerArmatureAdaptation_CT
+from case_study.units.ct_based.ObstacleFMU import ObstacleFMU
+from case_study.units.ct_based.PowerFMU import PowerFMU
+from case_study.units.ct_based.WindowFMU import WindowFMU
+from case_study.units.de_based.DriverControllerStatechartFMU_CTInOut import DriverControllerStatechartFMU_CTInOut
+from case_study.units.de_based.EnvironmentStatechartFMU_CTInOut import EnvironmentStatechartFMU_CTInOut
+
+
+NUM_RTOL = 1e-08
+NUM_ATOL = 1e-08
+
+l = logging.getLogger()
+l.setLevel(logging.DEBUG)
+
+cosim_step_size = 0.001
+num_internal_steps = 10
+stop_time = 10;
+
+environment = EnvironmentStatechartFMU_CTInOut("env", NUM_RTOL, NUM_ATOL)
+
+controller = DriverControllerStatechartFMU_CTInOut("controller", NUM_RTOL, NUM_ATOL)
+
+power = PowerFMU("power", NUM_RTOL, NUM_ATOL, cosim_step_size/num_internal_steps, 
+                     J=0.085, 
+                     b=5, 
+                     K=1.8, 
+                     R=0.15, 
+                     L=0.036,
+                     V_a=12)
+
+armature_threshold = 20.0
+adapt_armature = InacurateControllerArmatureAdaptation_CT("arm_adapt", NUM_RTOL, NUM_ATOL, armature_threshold, True)
+
+window_radius = 0.017
+
+window = WindowFMU("window", NUM_RTOL, NUM_ATOL, cosim_step_size/num_internal_steps, 
+                     J=0.085, 
+                     r=window_radius, 
+                     b = 150, 
+                     c = 1e3) # c = 1e5 makes this an unstable system.
+
+obstacle = ObstacleFMU("obstacle", NUM_RTOL, NUM_ATOL, cosim_step_size/num_internal_steps, 
+                     c=1e5, 
+                     fixed_x=0.45)
+
+environment.enterInitMode()
+controller.enterInitMode()
+power.enterInitMode()
+adapt_armature.enterInitMode()
+window.enterInitMode()
+obstacle.enterInitMode()
+
+# Solve initialisation.
+"""
+The initialisation may impose a completely different order for the execution of the FMUs.
+In this case we know that there is no direct feed-through between the power input and its outputs,
+    so we can safely set its initial state, get its output, and then compute all the other FMUs,
+    before setting the new inputs to the power.
+"""
+
+power.setValues(0, 0, {
+                                 power.omega: 0.0, 
+                                 power.theta: 0.0, 
+                                 power.i: 0.0
+                                 })
+pOut = power.getValues(0, 0, [power.omega, power.theta, power.i])
+
+window.setValues(0, 0, {window.omega_input: pOut[power.omega],
+                            window.theta_input: pOut[power.theta],
+                            window.theta: 0.0,
+                            window.omega: 0.0
+                            })
+wOut = window.getValues(0, 0, [window.tau, window.x])
+
+obstacle.setValues(0, 0, {obstacle.x: wOut[window.x]})
+oOut = obstacle.getValues(0, 0, [obstacle.F])
+
+adapt_armature.setValues(0, 0, {adapt_armature.armature_current: pOut[power.i],
+                                adapt_armature.out_obj: 0.0 })
+
+adaptArmOut = adapt_armature.getValues(0, 0, [adapt_armature.out_obj])
+
+envOut = environment.getValues(0, 0, [environment.out_up, environment.out_down])
+
+controller.setValues(0, 0, {controller.in_dup : envOut[environment.out_up],
+                            controller.in_ddown : envOut[environment.out_down],
+                            controller.in_obj : adaptArmOut[adapt_armature.out_obj]})
+
+cOut = controller.getValues(0, 0, [controller.out_up, controller.out_down])
+
+# Coupling equation for power
+power_tau = - ( wOut[window.tau] + window_radius * oOut[obstacle.F])
+
+# Finally set the other power inputs
+power.setValues(0, 0, {power.tau: power_tau, 
+                       power.up: cOut[controller.out_up],
+                       power.down: cOut[controller.out_down]})
+
+environment.exitInitMode()
+controller.exitInitMode()
+power.exitInitMode()
+adapt_armature.exitInitMode()
+window.exitInitMode()
+obstacle.exitInitMode()
+
+trace_i = [0.0]
+trace_omega = [0.0]
+trace_x = [0.0]
+trace_F = [0.0]
+times = [0.0]
+
+time = 0.0
+
+iteration = 0 # For now this is always zero
+
+for step in range(1, int(stop_time / cosim_step_size) + 1):
+    
+    # Note that despite the fact that there is no feedthrough between 
+    # the inputs and the outputs of the power system, 
+    # the internal solver would still benefit from an up-to-date 
+    # value given for the inputs. However, that creates an algebraic loop, 
+    # so for now, we just get old values for the inputs.
+    
+    # Apart from that, the code below is the same as the one in the initialization
+    
+    oOut = obstacle.getValues(step-1, iteration, [obstacle.F])
+    wOut = window.getValues(step-1, iteration, [window.tau, window.x])
+    cOut = controller.getValues(step-1, iteration, [controller.out_up, controller.out_down])
+    
+    # Coupling equation for power
+    power_tau = - ( wOut[window.tau] + window_radius * oOut[obstacle.F])
+    
+    # Set the delayed inputs to the power
+    power.setValues(step, iteration, {power.tau: power_tau, 
+                           power.up: cOut[controller.out_up],
+                           power.down: cOut[controller.out_down]})
+    
+    power.doStep(time, step, iteration, cosim_step_size)
+    
+    pOut = power.getValues(step, iteration, [power.omega, power.theta, power.i])
+    
+    window.setValues(step, iteration, {window.omega_input: pOut[power.omega],
+                            window.theta_input: pOut[power.theta]
+                            })
+    window.doStep(time, step, iteration, cosim_step_size) 
+    wOut = window.getValues(step, iteration, [window.tau, window.x])
+    
+    obstacle.setValues(step, iteration, {obstacle.x: wOut[window.x]})
+    obstacle.doStep(time, step, iteration, cosim_step_size) 
+    oOut = obstacle.getValues(step, iteration, [obstacle.F])
+    
+    adapt_armature.setValues(step, iteration, {adapt_armature.armature_current: pOut[power.i]})
+    adapt_armature.doStep(time, step, iteration, cosim_step_size) 
+    adaptArmOut = adapt_armature.getValues(step, iteration, [adapt_armature.out_obj])
+    
+    environment.doStep(time, step, iteration, cosim_step_size) 
+    envOut = environment.getValues(step, iteration, [environment.out_up, environment.out_down])
+    
+    # coupling equation for the input event of the controller
+    controller.setValues(step, iteration, {controller.in_dup : envOut[environment.out_up],
+                            controller.in_ddown : envOut[environment.out_down],
+                            controller.in_obj : adaptArmOut[adapt_armature.out_obj]})
+    controller.doStep(time, step, iteration, cosim_step_size) 
+    cOut = controller.getValues(step, iteration, [controller.out_up, controller.out_down])
+
+    # Coupling equation for power
+    power_tau = - ( wOut[window.tau] + window_radius * oOut[obstacle.F])
+    
+    # Finally set the other power inputs
+    """
+    The instruction below is not really needed, as power has already performed the step.
+    However, we leave it here because in case an algebraic loop were being solved,
+    this is where we would set the improved values for the power inputs, 
+    and check for convergence.
+    
+    """
+    power.setValues(step, iteration, {power.tau: power_tau, 
+                           power.up: cOut[controller.out_up],
+                           power.down: cOut[controller.out_down]})
+    
+    trace_omega.append(pOut[power.omega])
+    trace_i.append(pOut[power.i])
+    trace_x.append(wOut[window.x])
+    trace_F.append(oOut[obstacle.F])
+    time += cosim_step_size
+    times.append(time)
+
+color_pallete = [
+                "#e41a1c",
+                "#377eb8",
+                "#4daf4a",
+                "#984ea3",
+                "#ff7f00",
+                "#ffff33",
+                "#a65628",
+                "#f781bf"
+                 ]
+
+output_file("./results.html", title="Results")
+p = figure(title="Plot", x_axis_label='time', y_axis_label='')
+p.line(x=times, y=trace_omega, legend="trace_omega", color=color_pallete[0])
+p.line(x=times, y=trace_i, legend="trace_i", color=color_pallete[1])
+show(p)
+
+output_file("./results_x.html", title="Results")
+p = figure(title="Plot", x_axis_label='time', y_axis_label='')
+p.line(x=times, y=trace_x, legend="trace_x", color=color_pallete[2])
+show(p)
+
+output_file("./results_F.html", title="Results")
+p = figure(title="Plot", x_axis_label='time', y_axis_label='')
+p.line(x=times, y=trace_F, legend="trace_F", color=color_pallete[3])
+show(p)

+ 3 - 4
SemanticAdaptationForFMI/FMIAbstraction/src/case_study/scenarios/ControlledScenario_EventController.py

@@ -37,7 +37,7 @@ power = PowerFMU("power", NUM_RTOL, NUM_ATOL, cosim_step_size/num_internal_steps
                      K=1.8, 
                      R=0.15, 
                      L=0.036,
-                     V=12)
+                     V_a=12)
 
 armature_threshold = 20.0
 adapt_armature = InacurateControllerArmatureAdaptation_Event("arm_adapt", NUM_RTOL, NUM_ATOL, armature_threshold, True)
@@ -156,7 +156,6 @@ for step in range(1, int(stop_time / cosim_step_size) + 1):
     # Coupling equation for power
     power_tau = - ( wOut[window.tau] + window_radius * oOut[obstacle.F])
     
-    # Finally set the other power inputs
     power.setValues(step, 0, {power.tau: power_tau, 
                            power.up: adaptPowerOut[adapt_power_input.out_up],
                            power.down: adaptPowerOut[adapt_power_input.out_down]})
@@ -208,8 +207,8 @@ for step in range(1, int(stop_time / cosim_step_size) + 1):
     
     """
     power.setValues(step, 0, {power.tau: power_tau, 
-                           power.up: adapt_power_input.out_up,
-                           power.down: adapt_power_input.out_down})
+                           power.up: adaptPowerOut[adapt_power_input.out_up],
+                           power.down: adaptPowerOut[adapt_power_input.out_down]})
     
     trace_omega.append(pOut[power.omega])
     trace_i.append(pOut[power.i])

+ 1 - 1
SemanticAdaptationForFMI/FMIAbstraction/src/case_study/scenarios/NoObstacleTest.py

@@ -25,7 +25,7 @@ power = PowerFMU("power", 1e-08, 1e-08, cosim_step_size/num_internal_steps,
                      K=1.8, 
                      R=0.15, 
                      L=0.036,
-                     V=12)
+                     V_a=12)
 
 window = WindowFMU("window", 1e-08, 1e-08, cosim_step_size/num_internal_steps, 
                      J=0.085, 

+ 1 - 1
SemanticAdaptationForFMI/FMIAbstraction/src/case_study/scenarios/PowerFMU_Single.py

@@ -20,7 +20,7 @@ power = PowerFMU("power", 1e-08, 1e-05, 0.001,
                      K=1.8, 
                      R=0.15, 
                      L=0.036,
-                     V=12)
+                     V_a=12)
 
 power.enterInitMode()
 

+ 1 - 1
SemanticAdaptationForFMI/FMIAbstraction/src/case_study/scenarios/UncontrolledScenario.py

@@ -26,7 +26,7 @@ power = PowerFMU("power", 1e-08, 1e-08, cosim_step_size/num_internal_steps,
                      K=1.8, 
                      R=0.15, 
                      L=0.036,
-                     V=12)
+                     V_a=12)
 
 window_radius = 0.017
 

+ 96 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/case_study/units/adaptations/InacurateControllerArmatureAdaptation_CT.py

@@ -0,0 +1,96 @@
+import logging
+
+import numpy
+
+from abstract_units.AbstractSimulationUnit import AbstractSimulationUnit, \
+    STEP_ACCEPT
+
+
+l = logging.getLogger()
+
+class InacurateControllerArmatureAdaptation_CT(AbstractSimulationUnit):
+    """
+    This is the adaptation of the armature signal coming from the power system and into the controller statechart.
+    The input will be the armature continuous signal.
+    The output will be a pulse signal obj that can either be 0, or 1 whenever the absolute value of 
+        the armature signal exceeds a certain threshold.
+    
+    Note that we could have coded a generic signal crossing adaptation unit 
+        and there is a whole range of pathological cases that we are not dealing with now.
+        For these, see the implementation of the AbstractZeroCrossingBlock 
+        of the DiracCBD Simulator
+        
+    The detailed behaviour of this block is as follows:
+    ______________________________
+    f = InacurateControllerArmatureAdaptation_CT(...)
+    f.enterInitMode()
+    f.setValues(...,armature)
+        The FMU records this value in its internal state.
+    v = f.getValues(...)
+        v is zero at this point because there can be no crossing at time 0. 
+    f.exitInitMode()
+    
+    f.setValues(t,..., armature)
+        The FMU records the value as the input at time t.
+    f.doStep(..., H)
+        The FMU checks whether the current input has crossed the given threshold, 
+            taking into account the previous value of the input, 
+            already stored in the internal state.
+        The output value is then calculated and the current input moved into the internal state.
+    v = f.getValues(...)
+        Gets the output event. Can either be 0, or 1.
+    ______________________________
+    
+    """
+    
+    def __init__(self, name, num_rtol, num_atol, threshold, upward):
+        
+        self._num_rtol = num_rtol
+        self._num_atol = num_atol
+        
+        assert upward, "Not implemented yet."
+        
+        self.__crossUpward = upward
+        
+        self.__threshold = threshold
+        
+        self.armature_current = "armature_current"
+        self.out_obj = "out_event"
+        input_vars = [self.armature_current]
+        state_vars = [self.out_obj]
+        
+        algebraic_functions = {}
+        AbstractSimulationUnit.__init__(self, name, algebraic_functions, state_vars, input_vars)
+    
+    def _isClose(self, a, b):
+        return numpy.isclose(a,b, self._num_rtol, self._num_atol)
+    
+    def _biggerThan(self, a, b):
+        return not numpy.isclose(a,b, self._num_rtol, self._num_atol) and a > b
+    
+    def _doInternalSteps(self, time, step, iteration, cosim_step_size):
+        l.debug(">%s._doInternalSteps(%f, %d, %d, %f)", self._name, time, step, iteration, cosim_step_size)
+        
+        assert self._biggerThan(cosim_step_size, 0), "cosim_step_size too small: {0}".format(cosim_step_size)
+        assert iteration == 0, "Fixed point iterations not supported yet."
+        
+        current_input = self.getValues(step, iteration, self._getInputVars())[self.armature_current]
+        previous_input = self.getValues(step-1, iteration, self._getInputVars())[self.armature_current]
+        
+        output_value = 0
+        
+        l.debug("%s.previous_input=%f", self._name, previous_input)
+        l.debug("%s.current_input=%f", self._name, current_input)
+        
+        if (not self._biggerThan(previous_input, self.__threshold)) \
+                and self._biggerThan(current_input, self.__threshold) \
+                and self.__crossUpward:
+            output_value = 1
+        
+        l.debug("%s.output_value=%s", self._name, output_value)
+        self.setValues(step, iteration, {self.out_obj: output_value})
+        
+        l.debug("<%s._doInternalSteps() = (%s, %d)", self._name, STEP_ACCEPT, cosim_step_size)
+        return (STEP_ACCEPT, cosim_step_size)
+    
+    

+ 44 - 41
SemanticAdaptationForFMI/FMIAbstraction/src/case_study/units/de_based/DriverControllerStatechartFMU_CTInOut.py

@@ -41,7 +41,7 @@ class DriverControllerStatechartFMU_CTInOut(StatechartSimulationUnit_CTInOut):
         
         initial_state = "Initial"
         
-        def state_transition(current_state, inputs, previous_inputs, elapsed):
+        def state_transition(current_state, inputs, previous_inputs, elapsed, inputs_available):
             l.debug(">%s.state_transition(%s, %s, %f)", self._name, current_state, inputs, elapsed)
             
             output_assignment = {}
@@ -56,48 +56,51 @@ class DriverControllerStatechartFMU_CTInOut(StatechartSimulationUnit_CTInOut):
                 output_assignment[self.out_up] = 0
                 output_assignment[self.out_down] = 0
             elif current_state=="Neutral":
-                if inputs[self.in_dup] == 1:
-                    target_state = "Up"
-                    transition_taken = True
-                    trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
-                    output_assignment[self.out_up] = 1
-                elif inputs[self.in_ddown] == 1:
-                    target_state = "Down"
-                    transition_taken = True
-                    trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
-                    output_assignment[self.out_down] = 1
+                if inputs_available:
+                    if inputs[self.in_dup] == 1:
+                        target_state = "Up"
+                        transition_taken = True
+                        trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
+                        output_assignment[self.out_up] = 1
+                    elif inputs[self.in_ddown] == 1:
+                        target_state = "Down"
+                        transition_taken = True
+                        trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
+                        output_assignment[self.out_down] = 1
             elif current_state=="Up":
-                if inputs[self.in_obj] == 1:
-                    target_state = "Object"
-                    transition_taken = True
-                    trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
-                    output_assignment[self.out_up] = 0
-                    output_assignment[self.out_down] = 1
-                elif inputs[self.in_ddown] == 1:
-                    assert inputs[self.in_dup] == 0
-                    target_state = "Down"
-                    transition_taken = True
-                    trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
-                    output_assignment[self.out_up] = 0
-                    output_assignment[self.out_down] = 1
-                elif inputs[self.in_dup] == 0:
-                    target_state = "Neutral"
-                    transition_taken = True
-                    trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
-                    output_assignment[self.out_up] = 0
+                if inputs_available:
+                    if inputs[self.in_obj] == 1:
+                        target_state = "Object"
+                        transition_taken = True
+                        trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
+                        output_assignment[self.out_up] = 0
+                        output_assignment[self.out_down] = 1
+                    elif inputs[self.in_ddown] == 1:
+                        assert inputs[self.in_dup] == 0
+                        target_state = "Down"
+                        transition_taken = True
+                        trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
+                        output_assignment[self.out_up] = 0
+                        output_assignment[self.out_down] = 1
+                    elif inputs[self.in_dup] == 0:
+                        target_state = "Neutral"
+                        transition_taken = True
+                        trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
+                        output_assignment[self.out_up] = 0
             elif current_state=="Down":
-                if inputs[self.in_dup] == 1:
-                    assert inputs[self.in_ddown] == 0
-                    target_state = "Up"
-                    transition_taken = True
-                    trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
-                    output_assignment[self.out_up] = 1
-                    output_assignment[self.out_down] = 0
-                elif inputs[self.in_ddown] == 0:
-                    target_state = "Neutral"
-                    transition_taken = True
-                    trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
-                    output_assignment[self.out_down] = 0
+                if inputs_available:
+                    if inputs[self.in_dup] == 1:
+                        assert inputs[self.in_ddown] == 0
+                        target_state = "Up"
+                        transition_taken = True
+                        trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
+                        output_assignment[self.out_up] = 1
+                        output_assignment[self.out_down] = 0
+                    elif inputs[self.in_ddown] == 0:
+                        target_state = "Neutral"
+                        transition_taken = True
+                        trigger = StatechartSimulationUnit_CTInOut.TRIGGER_INPUT
+                        output_assignment[self.out_down] = 0
             elif current_state=="Object":
                 if self._biggerThan(elapsed, 1.0):
                     target_state = "Neutral"

+ 1 - 1
SemanticAdaptationForFMI/FMIAbstraction/src/case_study/units/de_based/EnvironmentStatechartFMU_CTInOut.py

@@ -24,7 +24,7 @@ class EnvironmentStatechartFMU_CTInOut(StatechartSimulationUnit_CTInOut):
         
         initial_state = "Initial"
                 
-        def state_transition(current_state, inputs, previous_inputs, elapsed):
+        def state_transition(current_state, inputs, previous_inputs, elapsed, inputs_available):
             l.debug(">%s.state_transition(%s, %s, %f)", self._name, current_state, inputs, elapsed)
             
             output_assignment = {}