From d93d230bdf3ca0a41fa4fbd9795d8e28f13bf436 Mon Sep 17 00:00:00 2001 From: fmessmer Date: Sat, 16 Jan 2021 16:39:45 +0100 Subject: [PATCH 1/4] copy-paste states to resolve cyclic dependency --- .../src/flexbe_testing/calculation_state.py | 41 +++++++++++++++++++ .../src/flexbe_testing/decision_state.py | 34 +++++++++++++++ .../test/selftest_behavior_sm.py | 4 +- 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 flexbe_testing/src/flexbe_testing/calculation_state.py create mode 100644 flexbe_testing/src/flexbe_testing/decision_state.py diff --git a/flexbe_testing/src/flexbe_testing/calculation_state.py b/flexbe_testing/src/flexbe_testing/calculation_state.py new file mode 100644 index 0000000..5bf16ea --- /dev/null +++ b/flexbe_testing/src/flexbe_testing/calculation_state.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +from flexbe_core import EventState, Logger + + +class CalculationState(EventState): + ''' + Implements a state that can perform a calculation based on userdata. + calculation is a function which takes exactly one parameter, input_value from userdata, + and its return value is stored in output_value after leaving the state. + + -- calculation function The function that performs the desired calculation. + It could be a private function (self.foo) manually defined in a behavior's source code + or a lambda function (e.g., lambda x: x^2, where x will be the input_value). + + ># input_value object Input to the calculation function. + + #> output_value object The result of the calculation. + + <= done Indicates completion of the calculation. + ''' + + def __init__(self, calculation): + super(CalculationState, self).__init__(outcomes=['done'], + input_keys=['input_value'], + output_keys=['output_value']) + self._calculation = calculation + self._calculation_result = None + + def execute(self, userdata): + userdata.output_value = self._calculation_result + # nothing to check + return 'done' + + def on_enter(self, userdata): + if self._calculation is not None: + try: + self._calculation_result = self._calculation(userdata.input_value) + except Exception as e: + Logger.logwarn('Failed to execute calculation function!\n%s' % str(e)) + else: + Logger.logwarn('Passed no calculation!') diff --git a/flexbe_testing/src/flexbe_testing/decision_state.py b/flexbe_testing/src/flexbe_testing/decision_state.py new file mode 100644 index 0000000..5b1d6e8 --- /dev/null +++ b/flexbe_testing/src/flexbe_testing/decision_state.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +from flexbe_core import EventState, Logger + + +class DecisionState(EventState): + ''' + Evaluates a condition function in order to return one of the specified outcomes. + This state can be used if the further control flow of the behavior depends on an advanced condition. + + -- outcomes string[] A list containing all possible outcomes of this state + -- conditions function Implements the condition check and returns one of the available outcomes. + Has to expect one parameter which will be set to input_value. + + ># input_value object Input to the condition function. + ''' + + def __init__(self, outcomes, conditions): + ''' + Constructor + ''' + super(DecisionState, self).__init__(outcomes=outcomes, + input_keys=['input_value']) + self._conditions = conditions + + def execute(self, userdata): + if self._conditions is not None: + outcome = None + try: + outcome = str(self._conditions(userdata.input_value)) + except Exception as e: + Logger.logwarn('Passed no function as predicate!\n%s' % str(e)) + outcome = None + if outcome is not None and outcome in self._outcomes: + return outcome diff --git a/flexbe_testing/src/flexbe_testing/test/selftest_behavior_sm.py b/flexbe_testing/src/flexbe_testing/test/selftest_behavior_sm.py index dd56e54..0681c80 100644 --- a/flexbe_testing/src/flexbe_testing/test/selftest_behavior_sm.py +++ b/flexbe_testing/src/flexbe_testing/test/selftest_behavior_sm.py @@ -8,8 +8,8 @@ ########################################################### from flexbe_core import Behavior, Autonomy, OperatableStateMachine, ConcurrencyContainer, PriorityContainer, Logger -from flexbe_states.calculation_state import CalculationState -from flexbe_states.decision_state import DecisionState +from flexbe_testing.calculation_state import CalculationState +from flexbe_testing.decision_state import DecisionState # Additional imports can be added inside the following tags # [MANUAL_IMPORT] From 92aaeda86202381acab7e48818d4c154a725477f Mon Sep 17 00:00:00 2001 From: fmessmer Date: Sun, 17 Jan 2021 12:20:34 +0100 Subject: [PATCH 2/4] fix missing dependency --- flexbe_onboard/package.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/flexbe_onboard/package.xml b/flexbe_onboard/package.xml index 3f20d4e..903f3e3 100644 --- a/flexbe_onboard/package.xml +++ b/flexbe_onboard/package.xml @@ -22,6 +22,7 @@ flexbe_core flexbe_msgs + flexbe_states rospy From 898703c68a15fdbd6dbb75a9bbb2ad4645e4e156 Mon Sep 17 00:00:00 2001 From: Philipp Schillinger Date: Mon, 18 Jan 2021 16:40:39 +0100 Subject: [PATCH 3/4] [flexbe_testing] Move state copies for testing into behavior --- .../src/flexbe_testing/calculation_state.py | 41 -------- .../src/flexbe_testing/decision_state.py | 34 ------- .../test/selftest_behavior_sm.py | 97 +++++++++---------- 3 files changed, 45 insertions(+), 127 deletions(-) delete mode 100644 flexbe_testing/src/flexbe_testing/calculation_state.py delete mode 100644 flexbe_testing/src/flexbe_testing/decision_state.py diff --git a/flexbe_testing/src/flexbe_testing/calculation_state.py b/flexbe_testing/src/flexbe_testing/calculation_state.py deleted file mode 100644 index 5bf16ea..0000000 --- a/flexbe_testing/src/flexbe_testing/calculation_state.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -from flexbe_core import EventState, Logger - - -class CalculationState(EventState): - ''' - Implements a state that can perform a calculation based on userdata. - calculation is a function which takes exactly one parameter, input_value from userdata, - and its return value is stored in output_value after leaving the state. - - -- calculation function The function that performs the desired calculation. - It could be a private function (self.foo) manually defined in a behavior's source code - or a lambda function (e.g., lambda x: x^2, where x will be the input_value). - - ># input_value object Input to the calculation function. - - #> output_value object The result of the calculation. - - <= done Indicates completion of the calculation. - ''' - - def __init__(self, calculation): - super(CalculationState, self).__init__(outcomes=['done'], - input_keys=['input_value'], - output_keys=['output_value']) - self._calculation = calculation - self._calculation_result = None - - def execute(self, userdata): - userdata.output_value = self._calculation_result - # nothing to check - return 'done' - - def on_enter(self, userdata): - if self._calculation is not None: - try: - self._calculation_result = self._calculation(userdata.input_value) - except Exception as e: - Logger.logwarn('Failed to execute calculation function!\n%s' % str(e)) - else: - Logger.logwarn('Passed no calculation!') diff --git a/flexbe_testing/src/flexbe_testing/decision_state.py b/flexbe_testing/src/flexbe_testing/decision_state.py deleted file mode 100644 index 5b1d6e8..0000000 --- a/flexbe_testing/src/flexbe_testing/decision_state.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -from flexbe_core import EventState, Logger - - -class DecisionState(EventState): - ''' - Evaluates a condition function in order to return one of the specified outcomes. - This state can be used if the further control flow of the behavior depends on an advanced condition. - - -- outcomes string[] A list containing all possible outcomes of this state - -- conditions function Implements the condition check and returns one of the available outcomes. - Has to expect one parameter which will be set to input_value. - - ># input_value object Input to the condition function. - ''' - - def __init__(self, outcomes, conditions): - ''' - Constructor - ''' - super(DecisionState, self).__init__(outcomes=outcomes, - input_keys=['input_value']) - self._conditions = conditions - - def execute(self, userdata): - if self._conditions is not None: - outcome = None - try: - outcome = str(self._conditions(userdata.input_value)) - except Exception as e: - Logger.logwarn('Passed no function as predicate!\n%s' % str(e)) - outcome = None - if outcome is not None and outcome in self._outcomes: - return outcome diff --git a/flexbe_testing/src/flexbe_testing/test/selftest_behavior_sm.py b/flexbe_testing/src/flexbe_testing/test/selftest_behavior_sm.py index 0681c80..4099914 100644 --- a/flexbe_testing/src/flexbe_testing/test/selftest_behavior_sm.py +++ b/flexbe_testing/src/flexbe_testing/test/selftest_behavior_sm.py @@ -1,81 +1,74 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -########################################################### -# WARNING: Generated code! # -# ************************** # -# Manual changes may get lost if file is generated again. # -# Only code inside the [MANUAL] tags will be kept. # -########################################################### -from flexbe_core import Behavior, Autonomy, OperatableStateMachine, ConcurrencyContainer, PriorityContainer, Logger -from flexbe_testing.calculation_state import CalculationState -from flexbe_testing.decision_state import DecisionState -# Additional imports can be added inside the following tags -# [MANUAL_IMPORT] +# +# Content is only intended for the flexbe_testing self-test +# -# [/MANUAL_IMPORT] +from flexbe_core import Behavior, Autonomy, OperatableStateMachine, EventState, Logger -''' -Created on Fri Apr 17 2020 -@author: Philipp Schillinger -''' class SelftestBehaviorSM(Behavior): - ''' - Simple behavior for the flexbe_testing self-test of behaviors. - ''' - + ''' Simple behavior for the flexbe_testing self-test of behaviors. ''' def __init__(self): super(SelftestBehaviorSM, self).__init__() self.name = 'Selftest Behavior' - - # parameters of this behavior self.add_parameter('value', 'wrong') - # references to used behaviors - - # Additional initialization code can be added inside the following tags - # [MANUAL_INIT] - - # [/MANUAL_INIT] - - # Behavior comments: - - - def create(self): - # x:30 y:365, x:130 y:365 _state_machine = OperatableStateMachine(outcomes=['finished', 'failed'], input_keys=['data'], output_keys=['result']) _state_machine.userdata.data = None _state_machine.userdata.result = None - # Additional creation code can be added inside the following tags - # [MANUAL_CREATE] - - # [/MANUAL_CREATE] - - with _state_machine: - # x:40 y:73 OperatableStateMachine.add('Modify Data', - CalculationState(calculation=lambda x: x * 2), + SelftestBehaviorSM._CalculationState(calculation=lambda x: x * 2), transitions={'done': 'Decide Param'}, autonomy={'done': Autonomy.Off}, remapping={'input_value': 'data', 'output_value': 'result'}) - - # x:37 y:201 OperatableStateMachine.add('Decide Param', - DecisionState(outcomes=['finished', 'failed'], conditions=lambda x: 'finished' if self.value == 'correct' else 'failed'), + SelftestBehaviorSM._DecisionState(outcomes=['finished', 'failed'], conditions=lambda x: 'finished' if self.value == 'correct' else 'failed'), transitions={'finished': 'finished', 'failed': 'failed'}, autonomy={'finished': Autonomy.Off, 'failed': Autonomy.Off}, remapping={'input_value': 'data'}) - - return _state_machine - - # Private functions can be added inside the following tags - # [MANUAL_FUNC] - - # [/MANUAL_FUNC] + class _CalculationState(EventState): + ''' Copy of the flexbe_states.CalculationState for use in the test behavior. ''' + + def __init__(self, calculation): + super(SelftestBehaviorSM._CalculationState, self).__init__(outcomes=['done'], input_keys=['input_value'], output_keys=['output_value']) + self._calculation = calculation + self._calculation_result = None + + def execute(self, userdata): + userdata.output_value = self._calculation_result + return 'done' + + def on_enter(self, userdata): + if self._calculation is not None: + try: + self._calculation_result = self._calculation(userdata.input_value) + except Exception as e: + Logger.logwarn('Failed to execute calculation function!\n%s' % str(e)) + else: + Logger.logwarn('Passed no calculation!') + + class _DecisionState(EventState): + ''' Copy of the flexbe_states.DecisionState for use in the test behavior. ''' + + def __init__(self, outcomes, conditions): + super(SelftestBehaviorSM._DecisionState, self).__init__(outcomes=outcomes, input_keys=['input_value']) + self._conditions = conditions + + def execute(self, userdata): + if self._conditions is not None: + outcome = None + try: + outcome = str(self._conditions(userdata.input_value)) + except Exception as e: + Logger.logwarn('Passed no function as predicate!\n%s' % str(e)) + outcome = None + if outcome is not None and outcome in self._outcomes: + return outcome From d8004f5b5eb91f6bf0eabda0a1bcd4b01e00230a Mon Sep 17 00:00:00 2001 From: Philipp Schillinger Date: Mon, 18 Jan 2021 16:48:36 +0100 Subject: [PATCH 4/4] Update flexbe_ci action to also run on PRs --- .github/workflows/flexbe_ci.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/flexbe_ci.yml b/.github/workflows/flexbe_ci.yml index 9b516ed..e5ce95f 100644 --- a/.github/workflows/flexbe_ci.yml +++ b/.github/workflows/flexbe_ci.yml @@ -1,11 +1,7 @@ # This is a basic workflow to help you get started with Actions name: FlexBE CI # Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: +on: [push, pull_request, workflow_dispatch] # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: