diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c5d283cd8..835b26711 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,18 @@
-stages:
- - test
+include:
+ - project: 'dev/sys/sopl/ar-dev'
+ # release branch
+ ref: 'release/3.x'
+ file:
+ - '/ci/templates/default.yml'
+
+variables:
+ AR_CI_CLANG_FORMAT_DISABLE: "true"
+ AR_CI_CLANG_TIDY_DISABLE: "true"
+ AR_CI_CPPCHECK_DISABLE: "true"
+ AR_CI_FLAKE8_DISABLE: "true"
+ AR_CI_YAPF_DISABLE: "true"
+ AR_CI_DOCS_DISABLE: "false"
+ AR_CI_DEPLOY_CONAN_DISABLE: "true"
# From https://docs.gitlab.com/ee/ci/merge_request_pipelines/#excluding-certain-jobs
.only-default: &only-default
@@ -15,19 +28,36 @@ stages:
- tags
- merge_requests
+docs_test:
+ extends: .docs_test
+ variables:
+ BUILD_DIR: build
+ CONAN_OPTIONS: ""
+ MAKE_CMD_DOCS: "make docs"
+
+pages:
+ extends: .docs_pages
+ variables:
+ BUILD_DIR: build
+ CONAN_OPTIONS: ""
+ MAKE_CMD_DOCS: "make docs"
+ except:
+ variables:
+ - $AR_CI_DOCS_DISABLE == "true"
+
test py2-tests:
<<: *only-default
- stage: test
+ stage: Test
tags:
- Agile_GUI_Docker
script:
- - pip2 install --user --force "pytest>=3.5,<5"
+ - pip2 install --user --force "pytest>=3.5,<5" lazy-object-proxy==1.5.0 PyYAML==5.1 yaml-configuration==0.2.5
- pip2 install pytest-runner==5.2 # this is py2 compatible, ver 5.3 is not
- xvfb-run -as "-screen 0 1920x1200x24" python2 setup.py pytest --addopts '-vx -m "(core or gui) and not unstable and not user_input"'
test py3-tests:
<<: *only-default
- stage: test
+ stage: Test
tags:
- Agile_GUI_Docker
script:
@@ -44,7 +74,7 @@ test py3-tests:
test py3-tests-core:
<<: *only-default
- stage: test
+ stage: Test
tags:
- Agile_GUI_Docker
script:
@@ -62,7 +92,7 @@ test py3-tests-core:
test py3-memory-test-core:
<<: *only-default
- stage: test
+ stage: Test
tags:
- Agile_GUI_Docker
script:
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
index 69987b2fa..105ce2da2 100644
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Start_GUI.xml b/.idea/runConfigurations/Start_GUI.xml
index d81f909d5..c34150791 100644
--- a/.idea/runConfigurations/Start_GUI.xml
+++ b/.idea/runConfigurations/Start_GUI.xml
@@ -6,7 +6,7 @@
-
+
@@ -16,6 +16,8 @@
-
+
+
+
-
+
\ No newline at end of file
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index b3fb966fc..5c4c4e35d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -5,17 +5,47 @@ Information about :ref:`RAFCON` changes in each release will be published here.
details can be found in the `GIT commit log `__.
-Patch releases 0.14.\*
-----------------------
+0.15.3
+"""""""
-Next Release
-""""""""""""
+- Bug Fixes:
+ - Fix bug in LoggingView, which freezes RAFCON
-- Features:
+
+0.15.2
+"""""""
- Bug Fixes:
+ - Make operations on the logging console thread-safe
+ - Define a new GUI config called 'MAX_LOGGING_BUFFER_LINES' that determines the maximum lines of the logging buffer. If the number of lines exceeds the config value, the old value will be deleted automatically via clipping.
-- Miscellaneous:
+
+0.15.1
+"""""""
+
+- Bug Fixes:
+ - Call 'show_notification' via 'idle_add'
+
+
+0.15.0
+"""""""
+
+- Features:
+ - Libraries can now be renamed and relocated. This includes single libraries, library folders and library root keys
+ - Ctrl+F can be used to search for states
+ - Missing libraries are supported better. In case a library cannot be found, the transitions and data-flows are preserved and added to the dummy-state, which is inserted instead of the library. Furthermore, the dummy-state has the same position and size as the old library state.
+ - New execution-history structure: Define specific consumers for in-memory-execution-history and file-system execution history. Furthermore, another hook was defined such that RAFCON plugins can be used to define further consumers. Watch out: the config values for controlling the execution history changed
+
+
+0.14.11
+"""""""
+
+- Features:
+ - Add search bar for lookup through state machine libraries
+ - Add find usage for finding the usages of state machine libraries
+
+- Bug Fixes:
+ - Fix handling of library interface change
0.14.10
@@ -37,7 +67,6 @@ Next Release
- Bug Fixes:
- Fix py2 support
- - Make rafcon installation robust against local PYTHONPATH settings + python packages in ~/.local
0.14.7
@@ -125,7 +154,7 @@ Maintenance release.
- Improvements:
- - most ``[PyGTK]DeprecatedWarning``s are fixed
+ - most ``[PyGTK]DeprecatedWarning``\s are fixed
- graphical editor: minor performance optimizations
- specify separators for JSON files: Python 3.4 no longer changes the whitespaces in state machine files
- override builtins string in JSON files: state machine files generated by Python 2 and 3 are now fully identical
@@ -137,7 +166,7 @@ Maintenance release.
- better defaults:
- root state is named "root state", further states "[state type] [states counter]"
- - script of ``ExecutionState``s uses more RAFCON features (``preemptive_wait``, return outcome name)
+ - script of ``ExecutionState``\s uses more RAFCON features (``preemptive_wait``, return outcome name)
- name of states uses full width of state
- provide RAFCON wheel file
@@ -225,7 +254,7 @@ Patch releases 0.13.\*
- optimize setup_requires in setup.py (faster installation)
- mark unreliable tests as unstable
- define timeouts for all tests
-
+
- Bug Fixes:
- :issue_ghe:`689` rafcon cannot run without numpy
@@ -241,15 +270,14 @@ Patch releases 0.13.\*
- add ExecutionTicker to see activity of state machine with high hierarchy depth
- Improvements:
-
+
- changing states (adding or removing) during step mode works now
- Bug Fixes:
- :issue_ghe:`678` script validation does not work
- :issue_ghe:`663` cannot rename connected data port of type object
- - :issue_ghe:`684` ``test_simple_execution_model_and_core_destruct_with_gui`` fails when running core & gui tests
- in a row
+ - :issue_ghe:`684` ``test_simple_execution_model_and_core_destruct_with_gui`` fails when running core & gui tests in a row
- fix pause and step mode behavior
- installation of fonts under Python 3
- various test fixed for Python 3
@@ -286,7 +314,7 @@ Patch releases 0.13.\*
- Changes:
- - Release correct style files
+ - Release correct style files
0.13.2
@@ -452,21 +480,21 @@ Patch releases 0.12.\*
"""""""
- Features:
-
+
- maintenance release
0.12.19
"""""""
- Bug Fixes:
-
+
- fix setup.py, sdist now working on pypi
0.12.18
"""""""
- Features:
-
+
- new shortcut open library state separately as state machine by default on 'Shift+Ctrl+Space' (shortcut works for multiple states, too)
- Improvements:
@@ -610,7 +638,7 @@ Patch releases 0.12.\*
- copy/paste for semantic data elements
- new config value SHOW_PATH_NAMES_IN_EXECUTION_HISTORY
- make library path in state editor overview selectable
-
+
- Bug Fixes:
- :issue_ghe:`503` scoped variable looks weird
@@ -923,7 +951,7 @@ Patch releases 0.10.\*
""""""
- Bug Fixes:
-
+
- make execution logs compatible with execution log viewer again
@@ -931,13 +959,13 @@ Patch releases 0.10.\*
""""""
- Improvements:
-
+
- complex actions(copy & paste, resize) are properly handled in gaphas and in the modification history
- :issue_ghe:`342` drag and drop now drops the state at the mouse position
- Bug Fixes:
-
- - show library content for OpenGL works again
+
+ - show library content for OpenGL works again
- add as template works again
- :issue_ghe:`343` Text field does not follow cursor
@@ -948,7 +976,7 @@ Patch releases 0.9.\*
"""""
- Improvements:
-
+
- execution history can be logged and is configurable via the config.yaml
0.9.7
@@ -1020,7 +1048,7 @@ Patch releases 0.9.\*
"""""
- Bug Fix
- - fix bad storage format in combination with wrong jsonconversion version
+ - fix bad storage format in combination with wrong jsonconversion version
0.9.0
"""""
@@ -1061,7 +1089,7 @@ Patch releases 0.9.\*
- :issue_ghe:`251`: Handles are added when hovering over a transition handle
- :issue_ghe:`259`: Do not hard code version in about dialog
- :issue_ghe:`260`: Meta data is loaded several times
-
+
Patch releases 0.8.\*
---------------------
@@ -1089,7 +1117,7 @@ Patch releases 0.8.\*
0.8.1
"""""
-
+
- Features:
- renaming of module paths: core instead of state machine; gui instead of mvc
@@ -1631,4 +1659,4 @@ Patch releases 0.2.\*
"""""
- First release version
-- Tool was renamed to RAFCON
+- Tool was renamed to RAFCON
\ No newline at end of file
diff --git a/CITATION.cff b/CITATION.cff
index f7ca780aa..29bd81353 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -3,7 +3,7 @@
cff-version: "1.0.3"
message: "If you use this software, please cite it using these metadata."
title: RAFCON
-version: 0.14
+version: 0.15
date-released: 2019-07-29
license: EPL-1.0
doi: "10.5281/zenodo.1493058"
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..f327f7ada
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+.PHONY: all docs build clean help
+
+
+all: docs
+
+
+# generate build folders
+build:
+ mkdir -p build/docs
+
+docs: build
+ echo "$(dirname "$1")"
+ pip3 install virtualenv || return -1
+ python3 -m virtualenv venv --system-site-packages || return -1
+ # Don't use the source command since not all shells support it.
+ . venv/bin/activate;\
+ pip3 install -r requirements-py3.txt;\
+ sphinx-build -v -b html doc build/docs/html
+
+clean:
+ rm -rf build
+ rm -rf venv
+
+help:
+ @echo "Available Targets:"
+ @echo "... all"
+ @echo "... docs"
\ No newline at end of file
diff --git a/VERSION b/VERSION
index b231a0aff..1985d9141 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.14.10
+0.15.3
diff --git a/doc/_static/execution_history.png b/doc/_static/execution_history.png
new file mode 100644
index 000000000..c493bb9a5
Binary files /dev/null and b/doc/_static/execution_history.png differ
diff --git a/doc/api/rafcon.core.execution.rst b/doc/api/rafcon.core.execution.rst
index e3b36d0eb..d37d68cff 100644
--- a/doc/api/rafcon.core.execution.rst
+++ b/doc/api/rafcon.core.execution.rst
@@ -4,9 +4,16 @@ Execution: ``execution``
.. contents::
:backlinks: top
-execution_history
------------------
-.. automodule:: rafcon.core.execution.execution_history
+base_execution_history
+----------------------
+.. automodule:: rafcon.core.execution.base_execution_history
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+consumer_manager
+----------------
+.. automodule:: rafcon.core.execution.consumer_manager
:members:
:undoc-members:
:show-inheritance:
@@ -18,9 +25,44 @@ state_machine_execution_engine
:undoc-members:
:show-inheritance:
-state_machine_status
---------------------
+execution_history_factory
+-------------------------
+.. automodule:: rafcon.core.execution.execution_history_factory
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+execution_history_items
+-----------------------
+.. automodule:: rafcon.core.execution.execution_history_items
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+execution_status
+----------------
.. automodule:: rafcon.core.execution.execution_status
:members:
:undoc-members:
:show-inheritance:
+
+in_memory_execution_history
+---------------------------
+.. automodule:: rafcon.core.execution.in_memory_execution_history
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+abstract_execution_history_consumer
+-----------------------------------
+.. automodule:: rafcon.core.execution.consumers.abstract_execution_history_consumer
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+file_system_consumer
+--------------------
+.. automodule:: rafcon.core.execution.consumers.file_system_consumer
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.core.rst b/doc/api/rafcon.core.rst
index ac60dec19..9534d148c 100644
--- a/doc/api/rafcon.core.rst
+++ b/doc/api/rafcon.core.rst
@@ -17,43 +17,87 @@ Subpackages
config
------
.. automodule:: rafcon.core.config
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
custom_exceptions
-----------------
.. automodule:: rafcon.core.custom_exceptions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
constants (in rafcon.core)
--------------------------
.. automodule:: rafcon.core.constants
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
global_variable_manager
-----------------------
.. automodule:: rafcon.core.global_variable_manager
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
id_generator
------------
.. automodule:: rafcon.core.id_generator
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
interface
---------
.. automodule:: rafcon.core.interface
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
library_manager
---------------
.. automodule:: rafcon.core.library_manager
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
script
------
.. automodule:: rafcon.core.script
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
singleton (in rafcon.core)
--------------------------
.. automodule:: rafcon.core.singleton
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
state_machine
-------------
.. automodule:: rafcon.core.state_machine
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
state_machine_manager
---------------------
.. automodule:: rafcon.core.state_machine_manager
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/doc/api/rafcon.core.state_elements.rst b/doc/api/rafcon.core.state_elements.rst
index bc3a33fd9..29401bed0 100644
--- a/doc/api/rafcon.core.state_elements.rst
+++ b/doc/api/rafcon.core.state_elements.rst
@@ -7,23 +7,47 @@ StateElements: ``state_elements``
DataFlow
--------
.. automodule:: rafcon.core.state_elements.data_flow
+ :members:
+ :undoc-members:
+ :show-inheritance:
-Data Ports
+
+DataPorts
----------
.. automodule:: rafcon.core.state_elements.data_port
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
Logical Ports
-------------
.. automodule:: rafcon.core.state_elements.logical_port
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
-Scope
------
+Scope (State Element)
+---------------------
.. automodule:: rafcon.core.state_elements.scope
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
Transition
----------
.. automodule:: rafcon.core.state_elements.transition
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
StateElement
------------
.. automodule:: rafcon.core.state_elements.state_element
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/doc/api/rafcon.core.states.rst b/doc/api/rafcon.core.states.rst
index 9eaae74e3..7aef9806b 100644
--- a/doc/api/rafcon.core.states.rst
+++ b/doc/api/rafcon.core.states.rst
@@ -7,31 +7,57 @@ States: ``states``
barrier_concurrency
-------------------
.. automodule:: rafcon.core.states.barrier_concurrency_state
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
concurrency_state
-----------------
.. automodule:: rafcon.core.states.concurrency_state
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
container_state
---------------
.. automodule:: rafcon.core.states.container_state
+ :members:
+ :undoc-members:
+ :show-inheritance:
execution_state
---------------
.. automodule:: rafcon.core.states.execution_state
+ :members:
+ :undoc-members:
+ :show-inheritance:
hierarchy_state
---------------
.. automodule:: rafcon.core.states.hierarchy_state
+ :members:
+ :undoc-members:
+ :show-inheritance:
library_state
-------------
.. automodule:: rafcon.core.states.library_state
+ :members:
+ :undoc-members:
+ :show-inheritance:
preemptive_concurrency
----------------------
.. automodule:: rafcon.core.states.preemptive_concurrency_state
+ :members:
+ :undoc-members:
+ :show-inheritance:
state
-----
.. automodule:: rafcon.core.states.state
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.core.storage.rst b/doc/api/rafcon.core.storage.rst
index 019366f74..ddc69dfc6 100644
--- a/doc/api/rafcon.core.storage.rst
+++ b/doc/api/rafcon.core.storage.rst
@@ -7,3 +7,6 @@ storage (in rafcon.core.storage)
Helper functions to store a statemachine in the local file system and load it from there
.. automodule:: rafcon.core.storage.storage
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.gui.controllers.rst b/doc/api/rafcon.gui.controllers.rst
index 5bcfa6770..6f0665cf1 100644
--- a/doc/api/rafcon.gui.controllers.rst
+++ b/doc/api/rafcon.gui.controllers.rst
@@ -25,78 +25,126 @@ MainWindowController (in main_window)
-------------------------------------
.. automodule:: rafcon.gui.controllers.main_window
+ :members:
+ :undoc-members:
+ :show-inheritance:
GraphicalEditorController (in graphical_editor_gaphas)
------------------------------------------------------
.. automodule:: rafcon.gui.controllers.graphical_editor_gaphas
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateMachinesEditorController (in state_machines_editor)
--------------------------------------------------------
.. automodule:: rafcon.gui.controllers.state_machines_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
ExecutionHistoryTreeController (in execution_history)
-----------------------------------------------------
.. automodule:: rafcon.gui.controllers.execution_history
+ :members:
+ :undoc-members:
+ :show-inheritance:
GlobalVariableManagerController (in global_variable_manager)
------------------------------------------------------------
.. automodule:: rafcon.gui.controllers.global_variable_manager
+ :members:
+ :undoc-members:
+ :show-inheritance:
LoggingConsoleController (in logging_console)
---------------------------------------------
.. automodule:: rafcon.gui.controllers.logging_console
+ :members:
+ :undoc-members:
+ :show-inheritance:
LibraryTreeController (in library_tree)
---------------------------------------
.. automodule:: rafcon.gui.controllers.library_tree
+ :members:
+ :undoc-members:
+ :show-inheritance:
MenuBarController (in menu_bar)
-------------------------------
.. automodule:: rafcon.gui.controllers.menu_bar
+ :members:
+ :undoc-members:
+ :show-inheritance:
NetworkController (in network_connections)
------------------------------------------
.. automodule:: rafcon.gui.controllers.network_connections
+ :members:
+ :undoc-members:
+ :show-inheritance:
ModificationHistoryTreeController (in modification_history)
-----------------------------------------------------------
.. automodule:: rafcon.gui.controllers.modification_history
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateMachineTreeController (in state_machine_tree)
--------------------------------------------------
.. automodule:: rafcon.gui.controllers.state_machine_tree
+ :members:
+ :undoc-members:
+ :show-inheritance:
StatesEditorController (in states_editor)
-----------------------------------------
.. automodule:: rafcon.gui.controllers.states_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
ToolBarController (in tool_bar)
-------------------------------
.. automodule:: rafcon.gui.controllers.tool_bar
+ :members:
+ :undoc-members:
+ :show-inheritance:
TopToolBarController (in top_tool_bar)
--------------------------------------
.. automodule:: rafcon.gui.controllers.top_tool_bar
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateIconController (in state_icons)
------------------------------------
.. automodule:: rafcon.gui.controllers.state_icons
+ :members:
+ :undoc-members:
+ :show-inheritance:
UndockedWindowController (in undocked_window)
---------------------------------------------
.. automodule:: rafcon.gui.controllers.undocked_window
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.gui.controllers.state_editor.rst b/doc/api/rafcon.gui.controllers.state_editor.rst
index edf5927fe..0bd73984c 100644
--- a/doc/api/rafcon.gui.controllers.state_editor.rst
+++ b/doc/api/rafcon.gui.controllers.state_editor.rst
@@ -9,43 +9,70 @@ LinkageOverviewController (in linkage_overview)
-----------------------------------------------
.. automodule:: rafcon.gui.controllers.state_editor.linkage_overview
+ :members:
+ :undoc-members:
+ :show-inheritance:
ScopedVariableListController (in scoped_variable_list)
------------------------------------------------------
.. automodule:: rafcon.gui.controllers.state_editor.scoped_variable_list
+ :members:
+ :undoc-members:
+ :show-inheritance:
SourceEditorController (in source_editor)
-----------------------------------------
.. automodule:: rafcon.gui.controllers.state_editor.source_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateDataFlowsListController (in data_flows)
--------------------------------------------
.. automodule:: rafcon.gui.controllers.state_editor.data_flows
+ :members:
+ :undoc-members:
+ :show-inheritance:
DescriptionEditorController (in description_editor)
---------------------------------------------------
.. automodule:: rafcon.gui.controllers.state_editor.description_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateEditorController (in state_editor)
---------------------------------------
.. automodule:: rafcon.gui.controllers.state_editor.state_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateOutcomesListController (in outcomes)
-----------------------------------------
.. automodule:: rafcon.gui.controllers.state_editor.outcomes
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateOverviewController (in overview)
-------------------------------------
.. automodule:: rafcon.gui.controllers.state_editor.overview
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateTransitionsListController (in transitions)
-----------------------------------------------
-.. automodule:: rafcon.gui.controllers.state_editor.transitions
\ No newline at end of file
+.. automodule:: rafcon.gui.controllers.state_editor.transitions
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.gui.controllers.utils.rst b/doc/api/rafcon.gui.controllers.utils.rst
index 3bf1ffce0..7710569e6 100644
--- a/doc/api/rafcon.gui.controllers.utils.rst
+++ b/doc/api/rafcon.gui.controllers.utils.rst
@@ -8,14 +8,22 @@ EditorController (in editor)
----------------------------
.. automodule:: rafcon.gui.controllers.utils.editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
ExtendedController (in extended_controller)
-------------------------------------------
.. automodule:: rafcon.gui.controllers.utils.extended_controller
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
SingleWidgetWindowController (in single_widget_window)
------------------------------------------------------
-.. automodule:: rafcon.gui.controllers.utils.single_widget_window
\ No newline at end of file
+.. automodule:: rafcon.gui.controllers.utils.single_widget_window
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.gui.models.rst b/doc/api/rafcon.gui.models.rst
index ed505e82b..aa80bb406 100644
--- a/doc/api/rafcon.gui.models.rst
+++ b/doc/api/rafcon.gui.models.rst
@@ -15,98 +15,141 @@ AbstractStateModel (in abstract_state)
--------------------------------------
.. automodule:: rafcon.gui.models.abstract_state
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateModel (in state)
---------------------
.. automodule:: rafcon.gui.models.state
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
ContainerStateModel (in container_state)
----------------------------------------
.. automodule:: rafcon.gui.models.container_state
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
LibraryStateModel (in library_state)
------------------------------------
.. automodule:: rafcon.gui.models.library_state
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateElementModel (in state_element)
------------------------------------
.. automodule:: rafcon.gui.models.state_element
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
TransitionModel (in transition)
-------------------------------
.. automodule:: rafcon.gui.models.transition
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
DataFlowModel (in data_flow)
----------------------------
.. automodule:: rafcon.gui.models.data_flow
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
DataPortModel (in data_port)
----------------------------
.. automodule:: rafcon.gui.models.data_port
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
ScopedVariableModel (in scoped_variable)
----------------------------------------
.. automodule:: rafcon.gui.models.scoped_variable
+ :members:
+ :undoc-members:
+ :show-inheritance:
Selection (in selection)
------------------------
.. automodule:: rafcon.gui.models.selection
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
LogicalPortModel (in logical_port)
----------------------------------
.. automodule:: rafcon.gui.models.logical_port
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateMachineModel (in state_machine)
------------------------------------
.. automodule:: rafcon.gui.models.state_machine
+ :members:
+ :undoc-members:
+ :show-inheritance:
ModificationsHistoryModel (in modification_history)
---------------------------------------------------
.. automodule:: rafcon.gui.models.modification_history
+ :members:
+ :undoc-members:
+ :show-inheritance:
AutoBackupModel (in auto_backup)
--------------------------------
.. automodule:: rafcon.gui.models.auto_backup
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateMachineManagerModel (in state_machine_manager)
---------------------------------------------------
.. automodule:: rafcon.gui.models.state_machine_manager
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
GlobalVariableManagerModel (in global_variable_manager)
-------------------------------------------------------
.. automodule:: rafcon.gui.models.global_variable_manager
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
LibraryManagerModel (in library_manager)
----------------------------------------
.. automodule:: rafcon.gui.models.library_manager
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateMachineExecutionEngineModel (in state_machine_execution_engine)
--------------------------------------------------------------------
.. automodule:: rafcon.gui.models.state_machine_execution_engine
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.gui.mygaphas.rst b/doc/api/rafcon.gui.mygaphas.rst
index c82702c51..e48546afa 100644
--- a/doc/api/rafcon.gui.mygaphas.rst
+++ b/doc/api/rafcon.gui.mygaphas.rst
@@ -21,12 +21,17 @@ Perpendicular Line
The base class for the :py:class:`rafcon.gui.mygaphas.items.connection.ConnectionView`.
.. automodule:: rafcon.gui.mygaphas.items.line
+ :members:
+ :undoc-members:
+ :show-inheritance:
ConnectionViews
^^^^^^^^^^^^^^^
.. automodule:: rafcon.gui.mygaphas.items.connection
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
PortViews
^^^^^^^^^
@@ -34,7 +39,9 @@ PortViews
Each port (income, outcome, data ports) are represented as a view element.
.. automodule:: rafcon.gui.mygaphas.items.ports
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateView and NameView
^^^^^^^^^^^^^^^^^^^^^^
@@ -44,8 +51,9 @@ Each :py:class:`rafcon.gui.mygaphas.items.state.StateView` holds a child item
repositioned.
.. automodule:: rafcon.gui.mygaphas.items.state
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
Utility functions
@@ -55,78 +63,94 @@ Enumerations
^^^^^^^^^^^^
.. automodule:: rafcon.gui.mygaphas.utils.enums
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
Helper methods for drawing operations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: rafcon.gui.mygaphas.utils.gap_draw_helper
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
General helper methods
^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: rafcon.gui.mygaphas.utils.gap_helper
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
aspect
------
.. automodule:: rafcon.gui.mygaphas.aspect
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
canvas
------
.. automodule:: rafcon.gui.mygaphas.canvas
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
connector
---------
.. automodule:: rafcon.gui.mygaphas.connector
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
constraint
----------
.. automodule:: rafcon.gui.mygaphas.constraint
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
guide
-----
.. automodule:: rafcon.gui.mygaphas.guide
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
painter
-------
.. automodule:: rafcon.gui.mygaphas.painter
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
segment
-------
.. automodule:: rafcon.gui.mygaphas.segment
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
tools
-----
.. automodule:: rafcon.gui.mygaphas.tools
-
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
view
----
.. automodule:: rafcon.gui.mygaphas.view
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.gui.views.rst b/doc/api/rafcon.gui.views.rst
index 40f05e5b5..691598737 100644
--- a/doc/api/rafcon.gui.views.rst
+++ b/doc/api/rafcon.gui.views.rst
@@ -16,63 +16,102 @@ GraphicalEditor (in graphical_editor)
-------------------------------------
.. automodule:: rafcon.gui.views.graphical_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
GraphicalEditor (in graphical_editor_gaphas)
--------------------------------------------
.. automodule:: rafcon.gui.views.graphical_editor_gaphas
+ :members:
+ :undoc-members:
+ :show-inheritance:
ExecutionHistoryTreeView (in execution_history)
-----------------------------------------------
.. automodule:: rafcon.gui.views.execution_history
+ :members:
+ :undoc-members:
+ :show-inheritance:
GlobalVariableEditorView (in global_variable_editor)
----------------------------------------------------
.. automodule:: rafcon.gui.views.global_variable_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
LibraryTreeView (in library_tree)
---------------------------------
.. automodule:: rafcon.gui.views.library_tree
+ :members:
+ :undoc-members:
+ :show-inheritance:
LoggingConsoleView (in logging_console)
---------------------------------------
.. automodule:: rafcon.gui.views.logging_console
+ :members:
+ :undoc-members:
+ :show-inheritance:
MainWindowView (in main_window)
-------------------------------
.. automodule:: rafcon.gui.views.main_window
+ :members:
+ :undoc-members:
+ :show-inheritance:
MenuBarView (in menu_bar)
-------------------------
.. automodule:: rafcon.gui.views.menu_bar
+ :members:
+ :undoc-members:
+ :show-inheritance:
ModificationHistoryView (in modification_history)
-------------------------------------------------
.. automodule:: rafcon.gui.views.modification_history
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateMachineTreeView (in state_machine_tree)
--------------------------------------------
.. automodule:: rafcon.gui.views.state_machine_tree
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateMachinesEditorView (in state_machines_editor)
--------------------------------------------------
.. automodule:: rafcon.gui.views.state_machines_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
StatesEditorView (in states_editor)
-----------------------------------
.. automodule:: rafcon.gui.views.states_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
ToolBarView (in tool_bar)
-------------------------
.. automodule:: rafcon.gui.views.tool_bar
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.gui.views.state_editor.rst b/doc/api/rafcon.gui.views.state_editor.rst
index 856c993cc..44564586d 100644
--- a/doc/api/rafcon.gui.views.state_editor.rst
+++ b/doc/api/rafcon.gui.views.state_editor.rst
@@ -10,31 +10,49 @@ StateEditorView (in state_editor)
A module to view the all aspects of a respective state.
.. automodule:: rafcon.gui.views.state_editor.state_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
LinkageOverviewView (in linkage_overview)
-----------------------------------------
.. automodule:: rafcon.gui.views.state_editor.linkage_overview
+ :members:
+ :undoc-members:
+ :show-inheritance:
InputPortsListView (in input_port_list)
---------------------------------------
.. automodule:: rafcon.gui.views.state_editor.input_port_list
+ :members:
+ :undoc-members:
+ :show-inheritance:
OutputPortsListView (in output_port_list)
-----------------------------------------
.. automodule:: rafcon.gui.views.state_editor.output_port_list
+ :members:
+ :undoc-members:
+ :show-inheritance:
ScopedVariablesListView (in scoped_variables_list)
--------------------------------------------------
.. automodule:: rafcon.gui.views.state_editor.scoped_variables_list
+ :members:
+ :undoc-members:
+ :show-inheritance:
SourceEditorView (in source_editor)
-----------------------------------
.. automodule:: rafcon.gui.views.state_editor.source_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateDataFlowsListView (in data_flows)
--------------------------------------
@@ -42,25 +60,40 @@ StateDataFlowsListView (in data_flows)
A module to view the data flow of a respective state (internal, external)
.. automodule:: rafcon.gui.views.state_editor.data_flows
+ :members:
+ :undoc-members:
+ :show-inheritance:
DescriptionEditorView (in description_editor)
---------------------------------------------
.. automodule:: rafcon.gui.views.state_editor.description_editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateOutcomesEditorView (in state_outcomes)
-------------------------------------------
.. automodule:: rafcon.gui.views.state_editor.outcomes
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateOverviewView (in state_overview)
-------------------------------------
.. automodule:: rafcon.gui.views.state_editor.overview
+ :members:
+ :undoc-members:
+ :show-inheritance:
StateTransitionsEditorView (in state_transitions)
-------------------------------------------------
A module to view the logical connections (transitions) of a respective state (internal, external).
-.. automodule:: rafcon.gui.views.state_editor.transitions
\ No newline at end of file
+.. automodule:: rafcon.gui.views.state_editor.transitions
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.gui.views.utils.rst b/doc/api/rafcon.gui.views.utils.rst
index 324df3097..bd740a40f 100644
--- a/doc/api/rafcon.gui.views.utils.rst
+++ b/doc/api/rafcon.gui.views.utils.rst
@@ -8,13 +8,22 @@ AboutDialogView (in about_dialog)
---------------------------------
.. automodule:: rafcon.gui.views.utils.about_dialog
+ :members:
+ :undoc-members:
+ :show-inheritance:
EditorView (in editor)
----------------------
.. automodule:: rafcon.gui.views.utils.editor
+ :members:
+ :undoc-members:
+ :show-inheritance:
SingleWidgetWindowView (in single_widget_window)
------------------------------------------------
.. automodule:: rafcon.gui.views.utils.single_widget_window
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/api/rafcon.utils.rst b/doc/api/rafcon.utils.rst
index 08b063bb1..edbf955de 100644
--- a/doc/api/rafcon.utils.rst
+++ b/doc/api/rafcon.utils.rst
@@ -7,76 +7,132 @@ Utilities: ``utils``
constants
---------
.. automodule:: rafcon.utils.constants
+ :members:
+ :undoc-members:
+ :show-inheritance:
decorators
----------
.. automodule:: rafcon.utils.decorators
-git s
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
dict_operations
---------------
.. automodule:: rafcon.utils.dict_operations
+ :members:
+ :undoc-members:
+ :show-inheritance:
execution_log
-------------
.. automodule:: rafcon.utils.execution_log
+ :members:
+ :undoc-members:
+ :show-inheritance:
filesystem
----------
.. automodule:: rafcon.utils.filesystem
+ :members:
+ :undoc-members:
+ :show-inheritance:
geometry
--------
.. automodule:: rafcon.utils.geometry
+ :members:
+ :undoc-members:
+ :show-inheritance:
hashable
--------
.. automodule:: rafcon.utils.hashable
+ :members:
+ :undoc-members:
+ :show-inheritance:
i18n
----
.. automodule:: rafcon.utils.i18n
+ :members:
+ :undoc-members:
+ :show-inheritance:
installation
------------
.. automodule:: rafcon.utils.installation
+ :members:
+ :undoc-members:
+ :show-inheritance:
log
---
.. automodule:: rafcon.utils.log
+ :members:
+ :undoc-members:
+ :show-inheritance:
log_helpers
-----------
.. automodule:: rafcon.utils.log_helpers
+ :members:
+ :undoc-members:
+ :show-inheritance:
multi_event
-----------
.. automodule:: rafcon.utils.multi_event
+ :members:
+ :undoc-members:
+ :show-inheritance:
-plugins
--------
+plugins (utils)
+---------------
.. automodule:: rafcon.utils.plugins
+ :members:
+ :undoc-members:
+ :show-inheritance:
profiler
--------
.. automodule:: rafcon.utils.profiler
+ :members:
+ :undoc-members:
+ :show-inheritance:
resources
---------
.. automodule:: rafcon.utils.resources
+ :members:
+ :undoc-members:
+ :show-inheritance:
storage_utils
-------------
.. automodule:: rafcon.utils.storage_utils
+ :members:
+ :undoc-members:
+ :show-inheritance:
timer
-----
.. automodule:: rafcon.utils.timer
+ :members:
+ :undoc-members:
+ :show-inheritance:
type_helpers
------------
.. automodule:: rafcon.utils.type_helpers
+ :members:
+ :undoc-members:
+ :show-inheritance:
vividict
--------
.. automodule:: rafcon.utils.vividict
-
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/concepts.rst b/doc/concepts.rst
index b0c777570..e037a9d27 100644
--- a/doc/concepts.rst
+++ b/doc/concepts.rst
@@ -218,3 +218,27 @@ left-hand side of the GUI. There you can also create new variables.
However, variables are not stored when saving state-machines. If you
want to have variables loaded with the state-machine, you have to create
those variables in an initial execution state.
+
+
+Execution History
+-----------------
+
+The general idea of the execution history is shown in the following figure:
+
+.. figure:: _static/execution_history.png
+ :alt: Screenshot of RAFCON with an example state machine
+ :width: 100 %
+ :align: center
+
+During execution, a start and an end event is created for each state.
+Subsequently, the events are forwarded via the ExecutionHistory
+(in case the IN_MEMORY_EXECUTION_HISTORY_ENABLE is enabled, the InMemoryExecutionHistory is used)
+to the ConsumerManager. The ConsumerManager distributes the events to different Consumers.
+RAFCON ships with a default Consumer called the FileSystemConsumer.
+If FILE_SYSTEM_EXECUTION_HISTORY_ENABLE is enabled the FileSystemConsumer will write all history on disk into a shelve file.
+The execution_log_viewer.py can then be used to analyze the execution history logs after untime.
+
+It is straightforward to create other consumers (e.g. for logging the execution-history via a middleware).
+Therefore, a plugin can be created that just works similar to the FileSystemConsumer.
+For writing a plugin, only the "pre_init" and the "register_execution_history_consumer" hook has to be created.
+The latter one has to care about that the consumer is registered at the ExecutionHistoryConsumerManager.
diff --git a/doc/configuration.rst b/doc/configuration.rst
index 59684794b..2da2353c5 100644
--- a/doc/configuration.rst
+++ b/doc/configuration.rst
@@ -14,8 +14,7 @@ Core Configuration
.. _core_config_example:
-Example
-"""""""
+Example:
A typical config file looks like this:
@@ -31,21 +30,23 @@ A typical config file looks like this:
"intermediate_level": "${RAFCON_LIB_PATH}/../examples/functionality_examples"
}
LIBRARY_RECOVERY_MODE: False
+ LOAD_SM_WITH_CHECKS: True
STORAGE_PATH_WITH_STATE_NAME: True
MAX_LENGTH_FOR_STATE_NAME_IN_STORAGE_PATH: None
NO_PROGRAMMATIC_CHANGE_OF_LIBRARY_STATES_PERFORMED: False
- EXECUTION_LOG_ENABLE: False
+ IN_MEMORY_EXECUTION_HISTORY_ENABLE: True
+ FILE_SYSTEM_EXECUTION_HISTORY_ENABLE: True
EXECUTION_LOG_PATH: "%RAFCON_TEMP_PATH_BASE/execution_logs"
EXECUTION_LOG_SET_READ_AND_WRITABLE_FOR_ALL: False
SCRIPT_RECOMPILATION_ON_STATE_EXECUTION: True
+ SCRIPT_COMPILE_ON_FILESYSTEM_LOAD: True
.. _core_config_docs:
-Documentation
-"""""""""""""
+Documentation:
In the following, all possible parameters are described, together with
their default value:
@@ -70,8 +71,17 @@ LIBRARY\_PATHS
LIBRARY\_RECOVERY\_MODE
| Type: boolean
| Default: ``False``
- | If this flag is activated, state machine with consistency erros concerning their data ports can be loaded.
- Erros are just printed out as warnings. This can be used to fix erroneous state machines.
+ | If this flag is activated, state machine with consistency errors concerning their data ports can be loaded.
+ Instead of raising exceptions only errors are printed. Invalid transitions and data-flows will just be removed.
+ This mode can be used to fix erroneous state machines.
+ Intermediate and expert users can also keep this setting enabled all the time.
+
+LOAD\_SM\_WITH\_CHECKS
+ | Type: boolean
+ | Default: ``True``
+ | If this flag is activated, every state is checked for consistency before loaded.
+ If set to false all consistency checks will be skipped. This leads to much faster loading times.
+ However, if there are consistency errors RAFCON tries to open the state machines and will fail.
STORAGE\_PATH\_WITH\_STATE\_NAME
| Type: boolean
@@ -91,6 +101,11 @@ NO\_PROGRAMMATIC\_CHANGE\_OF\_LIBRARY\_STATES\_PERFORMED
| Default: ``False``
| Set this to True if you can make sure that the interface of library states is not programmatically changed anywhere inside your state machines. This will speed up loading of libraries.
+EXECUTION\_HISTORY\_ENABLE
+ | Type: boolean
+ | Default: ``True``
+ | Enables execution history. The execution history is required for backward execution and execution logging to the file system.
+
EXECUTION\_LOG\_ENABLE
| Type: boolean
| Default: ``True``
@@ -114,7 +129,12 @@ SCRIPT\_RECOMPILATION\_ON\_STATE\_EXECUTION:
recommended to set the value to ``False``, causing a recompilation only when the execution of a state machine is
newly started, which is a bit faster and allows to share data between consecutive state executions.
-
+SCRIPT\_COMPILE\_ON\_FILESYSTEM\_LOAD:
+ | Type: boolean
+ | Default: ``True``
+ | If True, the script of an ``ExecutionState`` will be recompiled each time the state is loaded from file-system.
+ For faster loading times the setting can be changed to false.
+ Then, however, it might be the case that during runtime, script compilation error occur.
GUI Configuration
-----------------
@@ -281,8 +301,7 @@ A typical config file looks like this:
.. _gui_config_docs:
-Documentation
-"""""""""""""
+Documentation:
TYPE
| Type: String-constant
@@ -600,8 +619,7 @@ package, please check the `official documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/faq.rst b/doc/faq.rst
index b327e34d9..d5a2deaea 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -308,7 +308,7 @@ done if the type of the string matters.
.. _faq_event_based:
Why is RAFCON not event-based?
-"""""""""""""""""""""""""""""""""""""""""""""""""""
+""""""""""""""""""""""""""""""
**tl; dr**: RAFCON was created to enable the development of goal-driven behavior, in contrast to reactive behavior
(for which many frameworks rely on events).
@@ -354,6 +354,7 @@ Now try to answer the following questions, that would be raised during runtime:
3. What is the context (hierarchy level, possible predecessors, possible successors) of each of those state?
4. Since when do I listen to event_x? Until which state will I listen to event_x?
5. I receive an event that I cannot process know. I defer it to a later point in time (*event deferral*).
+
* How long are events valid (*event caching*)?
* Another event with the same type arrives meanwhile. Should I keep the old event (*event expiration*)?
* Another event, which is more important arrives. Should I react to the new event and preempt the current event handling (*event priorization*)?
@@ -482,6 +483,7 @@ that.
Open a new terminal, run the following command and restart RAFCON.
.. code:: bash
+
$ fdir="$HOME/.local/share/fonts" && mkdir -p $fdir && find "$(dirname $(which rafcon))/../share/fonts" -type f -name "*.otf" -exec cp -t $fdir {} + && unset fdir
This will copy the RAFCON font files from the install location to your local user, so the RAFCON GUI can load them.
diff --git a/doc/tutorials.rst b/doc/tutorials.rst
index 5ab37a241..77f421ce9 100644
--- a/doc/tutorials.rst
+++ b/doc/tutorials.rst
@@ -187,7 +187,7 @@ The following code blocks include code lines to generate the correct environment
in an e.g. Ubuntu setup, where the environment is statically specified
in the ~/.bashrc these environment generating commands can be omitted:
-.. code:: python
+.. code:: bash
rmpm_do env ros.indigo.desktop > /tmp/desktop.env
source /tmp/desktop.env
@@ -452,7 +452,7 @@ and the client:
If everything went fine, we should see below output in the debug console
of the client:
-.. code:: python
+.. code::
11:23:40 INFO - monitoring.client: Connect to server ('127.0.0.1', 9999)!
11:23:40 INFO - monitoring.client: self.connector
diff --git a/pytest.ini b/pytest.ini
index ab5c43ed3..8cfa5bc42 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -3,7 +3,7 @@
addopts = -x -p no:pytest_capturelog -p no:pytest_catchlog --tb=native
testpaths = tests
norecursedirs = .git all source share doc
-timeout = 40
+timeout = 60
timeout_method = signal
markers =
unstable: mark a test as not running reliable all the time
diff --git a/requirements-py3.txt b/requirements-py3.txt
index 3658177be..47e745962 100644
--- a/requirements-py3.txt
+++ b/requirements-py3.txt
@@ -2,19 +2,22 @@ astroid==1.6.5
backports.functools-lru-cache==1.5
configparser==3.5.0
decorator==4.3.0
-future==0.17.1
-gaphas==1.0.0
+future>=0.16,<0.18.0
+gaphas~=1.0.0
isort==4.3.4
-jsonconversion==0.2.12
-lazy-object-proxy==1.3.1
+jsonconversion~=0.2.12
mccabe==0.6.1
psutil==5.7.0
pycairo
PyGObject
-pylint==1.9.3
-python-gtkmvc3-dlr==1.0.1
+pylint==1.6
+python-gtkmvc3-dlr~=1.0.0
simplegeneric==0.8.1
singledispatch==3.4.0.3
six==1.12.0
wrapt==1.10.11
-yaml-configuration>=0.1.7
+yaml_configuration~=0.1
+pytest-runner
+libsass >= 0.15.0
+docutils==0.17.1
+
diff --git a/requirements.txt b/requirements.txt
index 83bef8536..cece90c98 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,20 +3,21 @@ backports.functools-lru-cache==1.5
configparser==3.5.0
decorator==4.3.0
enum34==1.1.6
-future==0.17.1
+future>=0.16,<0.18.0
futures==3.2.0
-gaphas==1.0.0rc1
-isort==4.3.4
+gaphas~=1.0.0
+isort==4.2.5
jsonconversion==0.2.9
-lazy-object-proxy==1.3.1
mccabe==0.6.1
psutil==5.7.0
pycairo
PyGObject
-pylint==1.9.3
-python-gtkmvc3-dlr==1.0.1
+pylint==1.6
+python-gtkmvc3-dlr~=1.0.0
simplegeneric==0.8.1
singledispatch==3.4.0.3
-six==1.12.0
+six~=1.11.0
wrapt==1.10.11
yaml-configuration>=0.1.7
+libsass >= 0.15.0
+docutils==0.17.1
diff --git a/setup.py b/setup.py
index 3f11e10c1..d87a9c399 100755
--- a/setup.py
+++ b/setup.py
@@ -64,20 +64,17 @@ def get_data_files():
if sys.version_info[0] < 3: # python2
- global_requirements = ['pylint>=1.6,<2', 'psutil', 'jsonconversion~=0.2.12', 'yaml_configuration~=0.1',
- 'pyyaml~=5.3.0',
+ global_requirements = ['pylint==1.6', 'psutil', 'jsonconversion~=0.2.12', 'yaml_configuration~=0.1',
'python-gtkmvc3-dlr~=1.0.0', 'gaphas~=1.0.0', 'future>=0.16,<0.18.0']
- # test_requirements = ['pytest>=3.5,<5', 'pytest-timeout', 'pytest-mock', 'pytest-faulthandler~=1.6.0',
- test_requirements = ['pytest>=3.5,<5', 'pytest-timeout', 'pytest-mock>=1.9.0,<3', 'pytest-faulthandler~=1.6.0',
+ test_requirements = ['pytest>=3.5,<5', 'pytest-timeout<2', 'pytest-mock>=1.9.0,<3', 'pytest-faulthandler~=1.6.0',
'importlib-metadata~=2.0.0', 'zipp~=1.0.0', 'pyparsing~=2.4.0', 'mock~=3.0.0',
- 'isort~=4.3.0',
+ 'isort==4.2.5',
'graphviz==0.16', 'pyuserinput', 'pandas', 'numpy']
- # setup_requirements = ['pytest-runner==4.5.1', 'libsass >= 0.15.0', 'six~=1.11.0', 'setuptools~=44.1.1']
- setup_requirements = ['pytest-runner==4.5.1', 'libsass >= 0.15.0', 'six~=1.11.0']
+ setup_requirements = ['pytest-runner==4.5.1', 'libsass >= 0.15.0', 'six~=1.11.0', 'pathlib']
else: # python3
- global_requirements = ['pylint>=1.6,<2', 'psutil', 'jsonconversion~=0.2.12', 'yaml_configuration~=0.1',
+ global_requirements = ['pylint==1.6', 'psutil', 'jsonconversion~=0.2.12', 'yaml_configuration~=0.1',
'python-gtkmvc3-dlr~=1.0.0', 'gaphas~=1.0.0', 'future>=0.16,<0.18.0']
- test_requirements = ['pytest>=3.5,<5', 'pytest-timeout', 'pytest-mock>=1.9.0,<3', 'pytest-faulthandler~=1.6.0',
+ test_requirements = ['pytest>=3.5,<5', 'pytest-timeout<2', 'pytest-mock>=1.9.0,<3', 'pytest-faulthandler~=1.6.0',
'graphviz==0.16', 'pyuserinput', 'pandas~=1.1.5', 'numpy~=1.19.5']
setup_requirements = ['pytest-runner', 'libsass >= 0.15.0']
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/core_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/core_data.json
index e469d1cf5..e0b27e0a4 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/core_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/core_data.json
@@ -1,26 +1,29 @@
{
"__jsonqualname__": "rafcon.core.states.barrier_concurrency_state.DeciderState",
"description": null,
+ "income": {
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Income"
+ },
"input_data_ports": {},
"name": "Decider",
"outcomes": {
"-2": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "preempted",
"outcome_id": -2
},
"-1": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "aborted",
"outcome_id": -1
},
"0": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "success",
"outcome_id": 0
},
"1": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "failure",
"outcome_id": 1
}
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/meta_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/meta_data.json
index d4e1722fe..d99602036 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/meta_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/meta_data.json
@@ -1,15 +1,6 @@
{
"gui": {
"editor_gaphas": {
- "income": {
- "rel_pos": {
- "__jsonqualname__": "__builtin__.tuple",
- "items": [
- 0.08,
- 0.4
- ]
- }
- },
"name": {
"rel_pos": {
"__jsonqualname__": "__builtin__.tuple",
@@ -42,6 +33,19 @@
}
}
},
+ "income": {
+ "gui": {
+ "editor_gaphas": {
+ "rel_pos": {
+ "__jsonqualname__": "__builtin__.tuple",
+ "items": [
+ 0.08,
+ 0.4
+ ]
+ }
+ }
+ }
+ },
"outcome-1": {
"gui": {
"editor_gaphas": {
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/script.py b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/script.py
index 0c35e7f3e..4ba223e93 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/script.py
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Decider_unique_decider_state_id/script.py
@@ -1,5 +1,8 @@
from builtins import str
-from exceptions import ZeroDivisionError
+import sys
+
+if sys.version_info[0] < 3:
+ from exceptions import ZeroDivisionError
def execute(self, inputs, outputs, gvm):
self.logger.debug("Executing decider state")
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/First_USMOTU/core_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/First_USMOTU/core_data.json
index a22198c3e..1b4be0cad 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/First_USMOTU/core_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/First_USMOTU/core_data.json
@@ -1,21 +1,24 @@
{
"__jsonqualname__": "rafcon.core.states.execution_state.ExecutionState",
"description": null,
+ "income": {
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Income"
+ },
"input_data_ports": {},
"name": "First",
"outcomes": {
"-2": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "preempted",
"outcome_id": -2
},
"-1": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "aborted",
"outcome_id": -1
},
"0": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "success",
"outcome_id": 0
}
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/First_USMOTU/meta_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/First_USMOTU/meta_data.json
index 12135cd5e..85f49894b 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/First_USMOTU/meta_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/First_USMOTU/meta_data.json
@@ -1,15 +1,6 @@
{
"gui": {
"editor_gaphas": {
- "income": {
- "rel_pos": {
- "__jsonqualname__": "__builtin__.tuple",
- "items": [
- 0.08,
- 0.4
- ]
- }
- },
"name": {
"rel_pos": {
"__jsonqualname__": "__builtin__.tuple",
@@ -42,6 +33,19 @@
}
}
},
+ "income": {
+ "gui": {
+ "editor_gaphas": {
+ "rel_pos": {
+ "__jsonqualname__": "__builtin__.tuple",
+ "items": [
+ 0.08,
+ 0.4
+ ]
+ }
+ }
+ }
+ },
"outcome-1": {
"gui": {
"editor_gaphas": {
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Second_MWCTHU/core_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Second_MWCTHU/core_data.json
index 72ad4f539..009d4c33f 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Second_MWCTHU/core_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Second_MWCTHU/core_data.json
@@ -1,21 +1,24 @@
{
"__jsonqualname__": "rafcon.core.states.execution_state.ExecutionState",
"description": null,
+ "income": {
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Income"
+ },
"input_data_ports": {},
"name": "Second",
"outcomes": {
"-2": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "preempted",
"outcome_id": -2
},
"-1": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "aborted",
"outcome_id": -1
},
"0": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "success",
"outcome_id": 0
}
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Second_MWCTHU/meta_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Second_MWCTHU/meta_data.json
index 05c3eec3b..d41e63820 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Second_MWCTHU/meta_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/Second_MWCTHU/meta_data.json
@@ -1,15 +1,6 @@
{
"gui": {
"editor_gaphas": {
- "income": {
- "rel_pos": {
- "__jsonqualname__": "__builtin__.tuple",
- "items": [
- 0.08,
- 0.4
- ]
- }
- },
"name": {
"rel_pos": {
"__jsonqualname__": "__builtin__.tuple",
@@ -42,6 +33,19 @@
}
}
},
+ "income": {
+ "gui": {
+ "editor_gaphas": {
+ "rel_pos": {
+ "__jsonqualname__": "__builtin__.tuple",
+ "items": [
+ 0.08,
+ 0.4
+ ]
+ }
+ }
+ }
+ },
"outcome-1": {
"gui": {
"editor_gaphas": {
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/core_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/core_data.json
index 049d77228..24a05767a 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/core_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/core_data.json
@@ -2,26 +2,29 @@
"__jsonqualname__": "rafcon.core.states.barrier_concurrency_state.BarrierConcurrencyState",
"data_flows": {},
"description": null,
+ "income": {
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Income"
+ },
"input_data_ports": {},
"name": "Barrier Concurrency State",
"outcomes": {
"-2": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "preempted",
"outcome_id": -2
},
"-1": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "aborted",
"outcome_id": -1
},
"0": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "success",
"outcome_id": 0
},
"1": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "failure",
"outcome_id": 1
}
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/meta_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/meta_data.json
index a80b37fd3..1b331f7a8 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/meta_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/Barrier_Concurrency_State_FUWGAD/meta_data.json
@@ -1,15 +1,6 @@
{
"gui": {
"editor_gaphas": {
- "income": {
- "rel_pos": {
- "__jsonqualname__": "__builtin__.tuple",
- "items": [
- 0.4,
- 2.0
- ]
- }
- },
"name": {
"rel_pos": {
"__jsonqualname__": "__builtin__.tuple",
@@ -42,6 +33,19 @@
}
}
},
+ "income": {
+ "gui": {
+ "editor_gaphas": {
+ "rel_pos": {
+ "__jsonqualname__": "__builtin__.tuple",
+ "items": [
+ 0.4,
+ 2.0
+ ]
+ }
+ }
+ }
+ },
"outcome-1": {
"gui": {
"editor_gaphas": {
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/core_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/core_data.json
index 7ba827a14..9429a3b40 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/core_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/core_data.json
@@ -2,26 +2,29 @@
"__jsonqualname__": "rafcon.core.states.hierarchy_state.HierarchyState",
"data_flows": {},
"description": null,
+ "income": {
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Income"
+ },
"input_data_ports": {},
"name": "Root State",
"outcomes": {
"-2": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "preempted",
"outcome_id": -2
},
"-1": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "aborted",
"outcome_id": -1
},
"0": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "success",
"outcome_id": 0
},
"1": {
- "__jsonqualname__": "rafcon.core.state_elements.outcome.Outcome",
+ "__jsonqualname__": "rafcon.core.state_elements.logical_port.Outcome",
"name": "failure",
"outcome_id": 1
}
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/meta_data.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/meta_data.json
index 3a9c14a86..a692e82ca 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/meta_data.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/Root_State_FMCFIB/meta_data.json
@@ -1,15 +1,6 @@
{
"gui": {
"editor_gaphas": {
- "income": {
- "rel_pos": {
- "__jsonqualname__": "__builtin__.tuple",
- "items": [
- 1.1274158626806232,
- 26.753692650115404
- ]
- }
- },
"name": {
"rel_pos": {
"__jsonqualname__": "__builtin__.tuple",
@@ -42,6 +33,19 @@
}
}
},
+ "income": {
+ "gui": {
+ "editor_gaphas": {
+ "rel_pos": {
+ "__jsonqualname__": "__builtin__.tuple",
+ "items": [
+ 1.1274158626806232,
+ 26.753692650115404
+ ]
+ }
+ }
+ }
+ },
"outcome-1": {
"gui": {
"editor_gaphas": {
diff --git a/share/rafcon/examples/tutorials/barrier_concurrency_state/statemachine.json b/share/rafcon/examples/tutorials/barrier_concurrency_state/statemachine.json
index 78d1d8160..c10a5e081 100644
--- a/share/rafcon/examples/tutorials/barrier_concurrency_state/statemachine.json
+++ b/share/rafcon/examples/tutorials/barrier_concurrency_state/statemachine.json
@@ -1,7 +1,7 @@
{
"creation_time": "2018-07-13 07:09:07",
- "last_update": "2018-08-02 07:23:08",
+ "last_update": "2021-10-20 21:12:23",
"root_state_storage_id": "Root_State_FMCFIB",
"state_machine_version": null,
- "used_rafcon_version": "0.12.17"
+ "used_rafcon_version": "0.14.11a1"
}
\ No newline at end of file
diff --git a/source/rafcon/core/config.yaml b/source/rafcon/core/config.yaml
index 8d6c51cf4..21af19c96 100644
--- a/source/rafcon/core/config.yaml
+++ b/source/rafcon/core/config.yaml
@@ -9,14 +9,14 @@ LIBRARY_PATHS: {
}
LIBRARY_RECOVERY_MODE: False
-LOAD_SM_WITH_CHECKS: False
+LOAD_SM_WITH_CHECKS: True
STORAGE_PATH_WITH_STATE_NAME: True
MAX_LENGTH_FOR_STATE_NAME_IN_STORAGE_PATH: None
NO_PROGRAMMATIC_CHANGE_OF_LIBRARY_STATES_PERFORMED: False
-EXECUTION_HISTORY_ENABLE: True
-EXECUTION_LOG_ENABLE: False
+IN_MEMORY_EXECUTION_HISTORY_ENABLE: True
+FILE_SYSTEM_EXECUTION_HISTORY_ENABLE: False
EXECUTION_LOG_PATH: "%RAFCON_TEMP_PATH_BASE/execution_logs"
EXECUTION_LOG_SET_READ_AND_WRITABLE_FOR_ALL: False
diff --git a/source/rafcon/core/execution/base_execution_history.py b/source/rafcon/core/execution/base_execution_history.py
new file mode 100644
index 000000000..9cd08d67a
--- /dev/null
+++ b/source/rafcon/core/execution/base_execution_history.py
@@ -0,0 +1,152 @@
+from rafcon.core.execution.execution_history_items import CallItem, ReturnItem, ConcurrencyItem, \
+ StateMachineStartItem, HistoryItem, ScopedDataItem
+from rafcon.core.execution.consumer_manager import ExecutionHistoryConsumerManager
+
+from rafcon.utils import log
+logger = log.get_logger(__name__)
+
+
+class BaseExecutionHistory(object):
+ """A class for the history of a state machine execution
+
+ :ivar initial_prev: optional link to a previous element for the first element pushed into this history of
+ type :class:`rafcon.core.execution.execution_history.HistoryItem`
+ """
+
+ def __init__(self, initial_prev=None, root_state_name="", consumer_manager=None):
+ if not consumer_manager:
+ self.consumer_manager = ExecutionHistoryConsumerManager(root_state_name)
+ else:
+ self.consumer_manager = consumer_manager
+ self.initial_prev = initial_prev
+ # saves the last history item in this variable in order to be able to get the pervious item id
+ self.last_history_item = None
+ self.destroyed = False
+ logger.debug("BaseExecutionHistory has been created")
+
+ def destroy(self):
+ """ Destroy the consumer manager and reset all variables
+ """
+ self.initial_prev = None
+ self.last_history_item = None
+ self.consumer_manager.stop_consumers()
+
+ def shutdown(self):
+ """ Destroy the consumer manager and reset all variables
+ """
+ # we don't want to keep anyting of the last run in memory, thus we can simply destroy the execution history
+ self.destroy()
+
+ # For now implement a "len" functions, as the GUI expects the history to have a len function
+ def __len__(self):
+ return 0
+
+ def get_last_history_item(self):
+ """Returns the history item that was added last
+
+ :return: History item added last
+ :rtype: rafcon.core.execution.execution_history_items.HistoryItem
+ """
+ return self.last_history_item
+
+ # TODO: delete the function from INEH? as it is redundant?
+ def _link_item(self, current_item):
+ """ Link the history item to the previous one
+
+ :param current_item: the history item
+ """
+ last_history_item = self.get_last_history_item()
+ if last_history_item is None:
+ current_item.prev = self.initial_prev
+ if last_history_item is not None:
+ current_item.prev = last_history_item
+ last_history_item.next = current_item
+ self.last_history_item = current_item
+
+ def feed_consumers(self, execution_history_item):
+ """ Add execution history item to the dedicated queue of all consumers
+ and notify their condition variables
+
+ :param execution_history_item: the execution history item
+ """
+ self.consumer_manager.add_history_item_to_queue(execution_history_item)
+
+ def push_call_history_item(self, state, call_type, state_for_scoped_data, input_data=None,
+ link_and_feed_item_to_consumers=True):
+ """ Adds a new call-history-item to the history item list
+
+ A call history items stores information about the point in time where a method (entry, execute,
+ exit) of a certain state was called.
+
+ :param state: the state that was called
+ :param call_type: the call type of the execution step,
+ i.e. if it refers to a container state or an execution state
+ :param state_for_scoped_data: the state of which the scoped data needs to be saved for further usages
+ (e.g. backward stepping)
+ :param link_and_feed_item_to_consumers: if the history item should be feed to all other consumers
+ """
+ from rafcon.core.states.library_state import LibraryState # delayed imported on purpose
+ if isinstance(state_for_scoped_data, LibraryState):
+ state_for_scoped_data = state_for_scoped_data.state_copy
+ history_item = CallItem(state, call_type, state_for_scoped_data, input_data, state.run_id)
+ if link_and_feed_item_to_consumers and self.consumer_manager.consumers_exist:
+ self._link_item(history_item)
+ self.feed_consumers(history_item)
+ return history_item
+
+ def push_return_history_item(self, state, call_type, state_for_scoped_data, output_data=None,
+ link_and_feed_item_to_consumers=True):
+ """ Adds a new return-history-item to the history item list
+
+ A return history items stores information about the point in time where a method (entry, execute,
+ exit) of a certain state returned.
+
+ :param state: the state that returned
+ :param call_type: the call type of the execution step,
+ i.e. if it refers to a container state or an execution state
+ :param state_for_scoped_data: the state of which the scoped data needs to be saved for further usages (e.g.
+ backward stepping)
+ """
+ from rafcon.core.states.library_state import LibraryState # delayed imported on purpose
+ if isinstance(state_for_scoped_data, LibraryState):
+ state_for_scoped_data = state_for_scoped_data.state_copy
+ history_item = ReturnItem(state, call_type, state_for_scoped_data, output_data,
+ state.run_id)
+ if link_and_feed_item_to_consumers and self.consumer_manager.consumers_exist:
+ self._link_item(history_item)
+ self.feed_consumers(history_item)
+ return history_item
+
+ def push_concurrency_history_item(self, state, number_concurrent_threads, link_and_feed_item_to_consumers=True):
+ """ Adds a new concurrency-history-item to the history item list
+
+ A concurrent history item stores information about the point in time where a certain number of states is
+ launched concurrently
+ (e.g. in a barrier concurrency state).
+
+ :param state: the state that launches the state group
+ :param number_concurrent_threads: the number of states that are launched
+ :param link_and_feed_item_to_consumers: if the history item should be feed to all other consumers
+ """
+ history_item = ConcurrencyItem(state, number_concurrent_threads, state.run_id, self.consumer_manager)
+ if link_and_feed_item_to_consumers and self.consumer_manager.consumers_exist:
+ self._link_item(history_item)
+ self.feed_consumers(history_item)
+ return history_item
+
+ def push_state_machine_start_history_item(self, state_machine, run_id, feed_item_to_consumers=True):
+ """ Adds a new state-machine-start-history-item to the history item list
+
+ A state machine starts history item stores information about the point in time where a state machine
+ started to run
+
+ :param state_machine: the state machine that started
+ :param run_id: the run id
+ :param feed_item_to_consumers: if the history item should be feed to all other consumers
+ """
+ history_item = StateMachineStartItem(state_machine, run_id)
+ if feed_item_to_consumers and self.consumer_manager.consumers_exist:
+ self._link_item(history_item)
+ self.feed_consumers(history_item)
+ return history_item
+
diff --git a/source/rafcon/core/execution/consumer_manager.py b/source/rafcon/core/execution/consumer_manager.py
new file mode 100644
index 000000000..08dc381cb
--- /dev/null
+++ b/source/rafcon/core/execution/consumer_manager.py
@@ -0,0 +1,118 @@
+import threading
+
+from queue import Queue
+
+from rafcon.core.config import global_config
+from rafcon.core.execution.consumers.file_system_consumer import FileSystemConsumer
+
+from rafcon.utils import plugins
+from rafcon.utils import log
+logger = log.get_logger(__name__)
+
+
+class ExecutionHistoryConsumerManager(object):
+ """ A class for managing all consumers including the consumer plugins
+ """
+
+ FILE_SYSTEM_CONSUMER_NAME = "file_system_consumer"
+
+ def __init__(self, root_state_name):
+ self.consumers = dict()
+ # Queue with infinite space
+ self.execution_history_item_queue = Queue()
+ self.condition = threading.Condition()
+ self.interrupt = False
+ self._consumers_exist = False
+ self._file_system_consumer_exists = False
+ if global_config.get_config_value("FILE_SYSTEM_EXECUTION_HISTORY_ENABLE", False):
+ self.register_consumer(self.FILE_SYSTEM_CONSUMER_NAME, FileSystemConsumer(root_state_name))
+ self._file_system_consumer_exists = True
+ plugins.run_hook("register_execution_history_consumer", self)
+ # Only have one thread here that will call the notify function of each consumer
+ # The advantage it that the consumer authors don't have to care about threading
+ # and don't have to care about when an item is popped from the queue
+ self.worker_thread = threading.Thread(target=self._feed_consumers)
+ self.worker_thread.start()
+
+ @property
+ def consumers_exist(self):
+ return self._consumers_exist
+
+ @consumers_exist.setter
+ def consumers_exist(self, value):
+ self._consumers_exist = value
+
+ @property
+ def file_system_consumer_exists(self):
+ """ Check if the file system consumer is activated
+ """
+ return self._file_system_consumer_exists
+
+ def get_file_system_consumer_file_name(self):
+ """ Get the filename of the shelve
+ """
+ if self.FILE_SYSTEM_CONSUMER_NAME in self.consumers.keys():
+ return self.consumers[self.FILE_SYSTEM_CONSUMER_NAME].filename
+
+ def stop_consumers(self):
+ """ Stop the working thread and unregister all consumers
+ """
+ self.stop_worker_thread()
+ for consumer in self.consumers.values():
+ self.unregister_consumer(consumer)
+
+ def stop_worker_thread(self):
+ """ Stop the working thread by setting interrupt to true
+ """
+ self.interrupt = True
+ with self.condition:
+ self.condition.notify()
+ self.worker_thread.join()
+
+ def register_consumer(self, consumer_name, consumer):
+ """ Register a specific consumer
+
+ :param consumer_name: the consumer name
+ :param consumer: an instance of the consumer
+ """
+ self.consumers_exist = True
+ self.consumers[consumer_name] = consumer
+ consumer.register()
+
+ def add_history_item_to_queue(self, execution_history_item):
+ """ Add execution history item to the dedicated queue of all consumers
+ and notify their condition variables
+
+ :param execution_history_item: the execution history item
+ """
+ with self.condition:
+ self.execution_history_item_queue.put(execution_history_item)
+ self.condition.notify()
+
+ def _feed_consumers(self):
+ """ Distribute the available execution history items to the consumers
+ """
+ while not self.interrupt:
+ with self.condition:
+ # Python 2.7 does not support wait_for unfortunately, so let's do it manually in a while loop
+ # self.condition.wait_for(lambda: not self.execution_history_item_queue.empty() or self.interrupt)
+ while self.execution_history_item_queue.empty() and not self.interrupt:
+ self.condition.wait()
+ while not self.execution_history_item_queue.empty():
+ next_execution_history_event = self.execution_history_item_queue.get(block=False)
+ self._notifyConsumers(next_execution_history_event)
+
+ def _notifyConsumers(self, execution_history_event):
+ """ Add execution history item to the dedicated queue of all consumers
+ """
+ for client in self.consumers.values():
+ client.enqueue(execution_history_event)
+
+ def unregister_consumer(self, consumer):
+ """ Unegister a specific consumer
+
+ :param consumer: an instance of the consumer
+ """
+ consumer.stop()
+ # Unregister the consumer after stopping the thread to avoid e.g., writing on the closed resources
+ consumer.unregister()
diff --git a/source/rafcon/core/execution/consumers/__init__.py b/source/rafcon/core/execution/consumers/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/source/rafcon/core/execution/consumers/abstract_execution_history_consumer.py b/source/rafcon/core/execution/consumers/abstract_execution_history_consumer.py
new file mode 100644
index 000000000..451b05623
--- /dev/null
+++ b/source/rafcon/core/execution/consumers/abstract_execution_history_consumer.py
@@ -0,0 +1,59 @@
+from queue import Queue
+from threading import Thread, Condition
+
+
+class AbstractExecutionHistoryConsumer(object):
+ """A class that should be the base for every defined consumer
+ """
+ def __init__(self):
+ self._queue = Queue()
+ self._thread = Thread(target=self.worker)
+ self._condition = Condition()
+ self._stop = False
+ self._thread.start()
+
+ def register(self):
+ """ Override the register for the consumer to run the required procedures when a consumer starts
+ """
+ raise NotImplementedError("The register function has to be implemented")
+
+ def consume(self, execution_history_item):
+ """ Override the register for the consumer to run the required procedures when a consumer wants to
+ consume an execution history item
+ """
+ raise NotImplementedError("The consume function has to be implemented")
+
+ def unregister(self):
+ """ Override the register for the consumer to run the required procedures when a consumer stops
+ """
+ raise NotImplementedError("The unregister function has to be implemented")
+
+ def enqueue(self, execution_history_item):
+ """ Add the execution history item to the local consumer queue
+
+ :param execution_history_item: the execution history item
+ """
+ with self._condition:
+ self._queue.put(execution_history_item, block=False)
+ self._condition.notify()
+
+ def worker(self):
+ """ Consume the available execution history item until the thread stops
+ """
+ while not self._stop:
+ with self._condition:
+ # Python 2.7 does not support wait_for unfortunately, so let's do it manually in a while loop
+ # self._condition.wait_for(lambda: not self._queue.empty() or self._stop)
+ while self._queue.empty() and not self._stop:
+ self._condition.wait()
+ while not self._queue.empty():
+ item = self._queue.get(block=False)
+ self.consume(item)
+
+ def stop(self):
+ """ Stop the consumer thread
+ """
+ self._stop = True
+ with self._condition:
+ self._condition.notify()
+ self._thread.join()
diff --git a/source/rafcon/core/execution/consumers/file_system_consumer.py b/source/rafcon/core/execution/consumers/file_system_consumer.py
new file mode 100644
index 000000000..8ea1e1161
--- /dev/null
+++ b/source/rafcon/core/execution/consumers/file_system_consumer.py
@@ -0,0 +1,113 @@
+import os
+import datetime
+import shelve
+import subprocess
+from threading import Lock
+from future.utils import native_str
+
+from rafcon.core.config import global_config
+from rafcon.core.execution.consumers.abstract_execution_history_consumer import AbstractExecutionHistoryConsumer
+from rafcon.utils.constants import RAFCON_TEMP_PATH_BASE
+from rafcon.utils import log
+logger = log.get_logger(__name__)
+
+
+class FileSystemConsumer(AbstractExecutionHistoryConsumer):
+ """ A class that consumes an execution history event and writes it onto the file system.
+ """
+ def __init__(self, root_state_name):
+ super(FileSystemConsumer, self).__init__()
+ self.destroyed = False
+ self.filename = self._get_storage_path_on_file_system(root_state_name)
+ self.store_lock = Lock()
+
+ def register(self):
+ """ Open the shelve file
+ """
+ try:
+ # 'c' for read/write/create
+ # protocol 2 cause of in some cases smaller file size
+ # writeback disabled, cause we don't need caching of entries in memory but continuous writes to the disk
+ self.store = shelve.open(self.filename, flag='c', protocol=2, writeback=False)
+ logger.debug('Openend log file for writing %s' % self.filename)
+ except Exception:
+ logger.exception('Exception:')
+
+ def consume(self, execution_history_item):
+ """ Write to the store variable corresponding to a shelve file
+ """
+ self._store_item(execution_history_item.history_item_id, execution_history_item.to_dict())
+
+ def unregister(self):
+ """ Flush & close the shelve file
+ """
+ set_read_and_writable_for_all = global_config.get_config_value("EXECUTION_LOG_SET_READ_AND_WRITABLE_FOR_ALL",
+ False)
+ self._flush()
+ self._close(set_read_and_writable_for_all)
+
+ @staticmethod
+ def _get_storage_path_on_file_system(root_state_name):
+ """ Get the shelve file of a specific state machine
+
+ :param root_state_name: the root name
+ """
+ base_dir = global_config.get_config_value("EXECUTION_LOG_PATH", "%RAFCON_TEMP_PATH_BASE/execution_logs")
+ if base_dir.startswith('%RAFCON_TEMP_PATH_BASE'):
+ base_dir = base_dir.replace('%RAFCON_TEMP_PATH_BASE', RAFCON_TEMP_PATH_BASE)
+ if not os.path.exists(base_dir):
+ os.makedirs(base_dir)
+ shelve_name = os.path.join(base_dir, '%s_rafcon_execution_log_%s.shelve' %
+ (str(datetime.datetime.now()),
+ root_state_name.replace(' ', '-')))
+ return shelve_name
+
+ def _store_item(self, key, value):
+ """ Flush & close the shelve file
+ """
+ with self.store_lock:
+ try:
+ self.store[native_str(key)] = value
+ except Exception:
+ logger.exception('Exception:')
+
+ def _flush(self):
+ """ Flush the shelve file
+ """
+ with self.store_lock:
+ try:
+ self.store.close()
+ self.store = shelve.open(self.filename, flag='c', protocol=2, writeback=False)
+ logger.debug('Flushed log file %s' % self.filename)
+ except Exception:
+ if self.destroyed:
+ pass # this is fine
+ else:
+ logger.exception('Exception:')
+
+ def _close(self, make_read_and_writable_for_all=False):
+ """ Close the shelve file
+ """
+ with self.store_lock:
+ try:
+ self.store.close()
+ logger.debug('Closed log file %s' % self.filename)
+ if make_read_and_writable_for_all:
+ ret = subprocess.call(['chmod', 'a+rw', self.filename])
+ if ret:
+ logger.debug('Could not make log file readable for all. chmod a+rw failed on %s.' % self.filename)
+ else:
+ logger.debug('Set log file readable for all via chmod a+rw, file %s' % self.filename)
+ except Exception:
+ logger.exception('Exception:')
+
+ def __del__(self):
+ """ Close the shelve file
+ """
+ with self.store_lock:
+ self.destroyed = True
+ try:
+ self.store.close()
+ logger.debug('Closed log file %s' % self.filename)
+ except Exception:
+ logger.exception('Exception:')
diff --git a/source/rafcon/core/execution/execution_engine.py b/source/rafcon/core/execution/execution_engine.py
index 3951a0581..b06a39696 100644
--- a/source/rafcon/core/execution/execution_engine.py
+++ b/source/rafcon/core/execution/execution_engine.py
@@ -67,6 +67,8 @@ def __init__(self, state_machine_manager):
self.state_counter = 0
self.state_counter_lock = Lock()
self.new_execution_command_handled = True
+ self.run_selected_done = False
+ self.run_selected_hierarchy_length = None
@Observable.observed
def pause(self):
@@ -230,7 +232,7 @@ def backward_step(self):
"""
logger.debug("Executing backward step ...")
- if not global_config.get_config_value("EXECUTION_HISTORY_ENABLE", True):
+ if not global_config.get_config_value("IN_MEMORY_EXECUTION_HISTORY_ENABLE", True):
logger.error("Backward stepping is not allowed if the execution histories are disabled")
return
@@ -306,6 +308,35 @@ def run_to_selected_state(self, path, state_machine_id=None):
self.run_to_states.append(path)
self._run_active_state_machine()
+ def run_selected_state(self, start_state_path=None, state_machine_id=None):
+ """Execute the selected state machine.
+ """
+ logger.debug("Run selected state")
+ if state_machine_id is not None:
+ self.state_machine_manager.active_state_machine_id = state_machine_id
+
+ self.run_to_states = []
+ # set appropriate start states
+ self.start_state_paths = []
+ if start_state_path:
+ path_list = start_state_path.split("/")
+ cur_path = ""
+ for path in path_list:
+ if cur_path == "":
+ cur_path = path
+ else:
+ cur_path = cur_path + "/" + path
+ self.start_state_paths.append(cur_path)
+
+ # set target states when execution should stop
+ self.run_to_states.append(start_state_path)
+
+ if self.finished_or_stopped():
+ self.set_execution_mode(StateMachineExecutionStatus.RUN_SELECTED_STATE)
+ self._run_active_state_machine()
+ else:
+ self.set_execution_mode(StateMachineExecutionStatus.RUN_SELECTED_STATE)
+
def _wait_while_in_pause_or_in_step_mode(self):
""" Waits as long as the execution_mode is in paused or step_mode
"""
@@ -334,20 +365,43 @@ def _wait_if_required(self, container_state, next_child_state_to_execute, woke_u
if next_child_state_to_execute:
next_child_state_path = next_child_state_to_execute.get_path()
if state_path == container_state.get_path():
- # the execution did a whole step_over inside hierarchy state "state" (case a) )
- # or a whole step_out into the hierarchy state "state" (case b) )
- # thus we delete its state path from self.run_to_states
- # and wait for another step (of maybe different kind)
- wait = True
- self.run_to_states.remove(state_path)
+ if self._status.execution_mode is StateMachineExecutionStatus.RUN_SELECTED_STATE:
+ # This is the very special case of having selected a direct hierarchy-child-state
+ # of a concurrency state as the state for the "run-selected-state" operation
+ # this basically means that we want to run the whole concurreny state
+ self._status.execution_mode = StateMachineExecutionStatus.FORWARD_OUT
+ self.run_to_states.remove(state_path)
+ # the new target state, when the execution should pause, is the parent of the concurrency state
+ self.run_to_states.append(container_state.parent.parent.get_path())
+ wait = False
+ else:
+ # the execution did a whole step_over inside hierarchy state "state" (case a) )
+ # or a whole step_out into the hierarchy state "state" (case b) )
+ # thus we delete its state path from self.run_to_states
+ # and wait for another step (of maybe different kind)
+ wait = True
+ self.run_to_states.remove(state_path)
break
elif state_path == next_child_state_path:
# this is the case that execution has reached a specific state explicitly marked via
# run_to_selected_state() (case c) )
- # if this is the case run_to_selected_state() is finished and the execution
- # has to wait for new execution commands
- wait = True
- self.run_to_states.remove(state_path)
+ if self._status.execution_mode is StateMachineExecutionStatus.RUN_SELECTED_STATE:
+ # we now reached the state that we wanted to execute using the "run-selected-state" feature
+ # now we just add one FORWARD_OVER step
+ self._status.execution_mode = StateMachineExecutionStatus.FORWARD_OVER
+ self.run_to_states.remove(state_path)
+ # add the container state to self.run_to_states
+ # this means that the execution will stop once it reaches the container state again
+ # this will be the case after the selected-state finished and execution is passed
+ # to the the parent of the selected state, which equals "container_state"
+ self.run_to_states.append(container_state.get_path())
+ wait = False
+ break
+ else:
+ # if this is the case run_to_selected_state() is finished and the execution
+ # has to wait for new execution commands
+ wait = True
+ self.run_to_states.remove(state_path)
break
# don't wait if its just a normal step
else:
@@ -520,9 +574,6 @@ def recompile_execution_state_scripts(state):
state_machine = self.state_machine_manager.get_active_state_machine()
recompile_execution_state_scripts(state_machine.root_state)
-
-
-
@Observable.observed
def set_execution_mode(self, execution_mode, notify=True):
""" An observed setter for the execution mode of the state machine status. This is necessary for the
diff --git a/source/rafcon/core/execution/execution_history.py b/source/rafcon/core/execution/execution_history.py
deleted file mode 100644
index 8f1470ab4..000000000
--- a/source/rafcon/core/execution/execution_history.py
+++ /dev/null
@@ -1,496 +0,0 @@
-# Copyright (C) 2014-2018 DLR
-#
-# All rights reserved. This program and the accompanying materials are made
-# available under the terms of the Eclipse Public License v1.0 which
-# accompanies this distribution, and is available at
-# http://www.eclipse.org/legal/epl-v10.html
-#
-# Contributors:
-# Franz Steinmetz
-# Rico Belder
-# Sebastian Brunner
-# Sebastian Riedel
-# ried_sa
-
-"""
-.. module:: execution_history
- :synopsis: A module for the history of one thread during state machine execution
-
-"""
-from future.utils import native_str
-from builtins import object
-from builtins import range
-from builtins import str
-import time
-import copy
-from collections import Iterable, Sized
-import json
-from jsonconversion.decoder import JSONObjectDecoder
-from jsonconversion.encoder import JSONObjectEncoder
-
-import shelve
-from threading import Lock
-from enum import Enum
-from gtkmvc3.observable import Observable
-
-from rafcon.core.id_generator import history_item_id_generator
-from rafcon.utils import log
-logger = log.get_logger(__name__)
-import os
-import subprocess
-import pickle
-from weakref import ref
-
-
-class ExecutionHistoryStorage(object):
- def __init__(self, filename):
- self.filename = filename
- self.store_lock = Lock()
- try:
- # 'c' for read/write/create
- # protocol 2 cause of in some cases smaller file size
- # writeback disabled, cause we don't need caching of entries in memory but continuous writes to the disk
- self.store = shelve.open(filename, flag='c', protocol=2, writeback=False)
- logger.debug('Openend log file for writing %s' % self.filename)
- except Exception:
- logger.exception('Exception:')
-
- def store_item(self, key, value):
- with self.store_lock:
- try:
- self.store[native_str(key)] = value
- except Exception:
- logger.exception('Exception:')
-
- def flush(self):
- with self.store_lock:
- try:
- self.store.close()
- self.store = shelve.open(self.filename, flag='c', protocol=2, writeback=False)
- logger.debug('Flushed log file %s' % self.filename)
- except Exception:
- if self.destroyed:
- pass # this is fine
- else:
- logger.exception('Exception:')
-
- def close(self, make_read_and_writable_for_all=False):
- with self.store_lock:
- try:
- self.store.close()
- logger.debug('Closed log file %s' % self.filename)
- if make_read_and_writable_for_all:
- ret = subprocess.call(['chmod', 'a+rw', self.filename])
- if ret:
- logger.debug('Could not make log file readable for all. chmod a+rw failed on %s.' % self.filename)
- else:
- logger.debug('Set log file readable for all via chmod a+rw, file %s' % self.filename)
- except Exception:
- logger.exception('Exception:')
-
- def __del__(self):
- with self.store_lock:
- self.destroyed = True
- try:
- self.store.close()
- logger.debug('Closed log file %s' % self.filename)
- except Exception:
- logger.exception('Exception:')
-
-
-class ExecutionHistory(Observable, Iterable, Sized):
- """A class for the history of a state machine execution
-
- It stores all history elements in a stack wise fashion.
-
- :ivar initial_prev: optional link to a previous element for the first element pushed into this history of
- type :class:`rafcon.core.execution.execution_history.HistoryItem`
- """
-
- def __init__(self, initial_prev=None):
- super(ExecutionHistory, self).__init__()
- self._history_items = []
- self.initial_prev = initial_prev
- self.execution_history_storage = None
-
- def destroy(self):
- # logger.verbose("Destroy execution history!")
- if self.execution_history_storage:
- self.execution_history_storage.close()
- self.execution_history_storage = None
- if len(self._history_items) > 0:
- if self._history_items[0]:
- execution_history_iterator = iter(self)
- for history_item in execution_history_iterator:
- history_item.destroy()
- self.destroyed = True
- self._history_items = None
- self.initial_prev = None
-
- def __iter__(self):
- return iter(self._history_items)
-
- def set_execution_history_storage(self, execution_history_storage):
- self.execution_history_storage = execution_history_storage
-
- def __len__(self):
- return len(self._history_items)
-
- def __getitem__(self, index):
- return self._history_items[index]
-
- def get_last_history_item(self):
- """Returns the history item that was added last
-
- :return: History item added last
- :rtype: HistoryItem
- """
- try:
- return self[-1]
- except IndexError: # this is the case for the very first executed state
- return None
-
- def _push_item(self, last_history_item, current_item):
- if last_history_item is None:
- current_item.prev = self.initial_prev
- if last_history_item is not None:
- last_history_item.next = current_item
- if self.execution_history_storage is not None:
- self.execution_history_storage.store_item(current_item.history_item_id, current_item.to_dict())
- try:
- self._history_items.append(current_item)
- except AttributeError:
- if self.destroyed:
- pass # this is fine
- else:
- raise
- return current_item
-
- @Observable.observed
- def push_call_history_item(self, state, call_type, state_for_scoped_data, input_data=None):
- """Adds a new call-history-item to the history item list
-
- A call history items stores information about the point in time where a method (entry, execute,
- exit) of certain state was called.
-
- :param state: the state that was called
- :param call_type: the call type of the execution step,
- i.e. if it refers to a container state or an execution state
- :param state_for_scoped_data: the state of which the scoped data needs to be saved for further usages
- (e.g. backward stepping)
- """
- last_history_item = self.get_last_history_item()
- from rafcon.core.states.library_state import LibraryState # delayed imported on purpose
- if isinstance(state_for_scoped_data, LibraryState):
- state_for_scoped_data = state_for_scoped_data.state_copy
- return_item = CallItem(state, last_history_item, call_type, state_for_scoped_data, input_data,
- state.run_id)
- return self._push_item(last_history_item, return_item)
-
- @Observable.observed
- def push_return_history_item(self, state, call_type, state_for_scoped_data, output_data=None):
- """Adds a new return-history-item to the history item list
-
- A return history items stores information about the point in time where a method (entry, execute,
- exit) of certain state returned.
-
- :param state: the state that returned
- :param call_type: the call type of the execution step,
- i.e. if it refers to a container state or an execution state
- :param state_for_scoped_data: the state of which the scoped data needs to be saved for further usages (e.g.
- backward stepping)
- """
- last_history_item = self.get_last_history_item()
- from rafcon.core.states.library_state import LibraryState # delayed imported on purpose
- if isinstance(state_for_scoped_data, LibraryState):
- state_for_scoped_data = state_for_scoped_data.state_copy
- return_item = ReturnItem(state, last_history_item, call_type, state_for_scoped_data, output_data,
- state.run_id)
- return self._push_item(last_history_item, return_item)
-
- @Observable.observed
- def push_concurrency_history_item(self, state, number_concurrent_threads):
- """Adds a new concurrency-history-item to the history item list
-
- A concurrent history item stores information about the point in time where a certain number of states is
- launched concurrently
- (e.g. in a barrier concurrency state).
-
- :param state: the state that launches the state group
- :param number_concurrent_threads: the number of states that are launched
- """
- last_history_item = self.get_last_history_item()
- return_item = ConcurrencyItem(state, self.get_last_history_item(),
- number_concurrent_threads, state.run_id,
- self.execution_history_storage)
- return self._push_item(last_history_item, return_item)
-
- @Observable.observed
- def push_state_machine_start_history_item(self, state_machine, run_id):
- return_item = StateMachineStartItem(state_machine, run_id)
- if self.execution_history_storage is not None:
- self.execution_history_storage.store_item(return_item.history_item_id, return_item.to_dict())
- self._history_items.append(return_item)
- return return_item
-
- @Observable.observed
- def pop_last_item(self):
- """Delete and returns the last item of the history item list.
-
- :return: History item added last
- :rtype: HistoryItem
- """
- try:
- return self._history_items.pop()
- except IndexError:
- logger.error("No item left in the history item list in the execution history.")
- return None
-
-
-class HistoryItem(object):
- """Class representing an entry within the history
-
- An abstract class that serves as a data structure to hold all important information of a certain point in time
- during the execution of a state machine. A history item is an element in a doubly linked history item list.
-
- :ivar state_reference: a reference to the state performing a certain action that is going to be saved
- :ivar path: the state path
- :ivar timestamp: the time of the call/return
- :ivar prev: the previous history item
- :ivar next: the next history item
- """
-
- def __init__(self, state, prev, run_id):
- self._state_reference = state
- self.path = copy.deepcopy(state.get_path())
- self.timestamp = time.time()
- self.run_id = run_id
- self.prev = prev
- self.next = None
- self.history_item_id = history_item_id_generator()
- self.state_type = str(type(state).__name__)
-
- def destroy(self):
- self._state_reference = None
- self.path = None
- self.timestamp = None
- self.run_id = None
- self.prev = None
- self.next = None
- self.history_item_id = None
- self.state_type = None
-
- @property
- def state_reference(self):
- """Property for the state_reference field
- """
- return self._state_reference
-
- def __str__(self):
- return "HistoryItem with reference state name %s (time: %s)" % (self.state_reference.name, self.timestamp)
-
- def to_dict(self):
- record = dict()
-
- # here always the correct path is desired
- record['path'] = self.state_reference.get_path()
- record['path_by_name'] = self.state_reference.get_path(by_name=True)
- record['state_type'] = str(type(self.state_reference).__name__)
-
- from rafcon.core.states.library_state import LibraryState # delayed imported on purpose
- if isinstance(self.state_reference, LibraryState):
- # in case of a Library State, all the data of the library itself should be used
- record['is_library'] = True
- # TODO: rename key to library_root_state_name? However, this will break many analysis scripts => next minor
- record['library_state_name'] = self.state_reference.state_copy.name
- record['library_name'] = self.state_reference.library_name
- record['library_path'] = self.state_reference.library_path
- target_state = self.state_reference.state_copy
- else:
- record['is_library'] = False
- record['library_state_name'] = None
- record['library_name'] = None
- record['library_path'] = None
- target_state = self.state_reference
-
- # there are 3 names of interest:
- # self.state_reference.library_name (<- name of the library on the filesystem)
- # self.state_reference.name (<- name of the user when using a library and changing the name)
- # self.state_reference.state_copy.name (<- the name of the library root state)
- record['state_name'] = self.state_reference.name
- record['timestamp'] = self.timestamp
- record['run_id'] = self.run_id # library state and state copy have the same run_id
- record['history_item_id'] = self.history_item_id
-
- # semantic data
- semantic_data_dict = {}
- for k, v in target_state.semantic_data.items():
- try:
- semantic_data_dict[k] = pickle.dumps(v)
- except Exception as e:
- semantic_data_dict['!' + k] = (str(e), str(v))
- record['semantic_data'] = semantic_data_dict
-
- record['description'] = target_state.description
-
- if self.prev is not None:
- record['prev_history_item_id'] = self.prev.history_item_id
- else:
- record['prev_history_item_id'] = None
- # store the specialized class name as item_type,
- # e.g. CallItem, ReturnItem, StatemachineStartItem when saved
- record['item_type'] = self.__class__.__name__
- return record
-
-
-class StateMachineStartItem(HistoryItem):
- def __init__(self, state_machine, run_id):
- HistoryItem.__init__(self, state_machine.root_state, None, run_id)
- from rafcon.core.state_machine import StateMachine
- self.sm_dict = StateMachine.state_machine_to_dict(state_machine)
- self.prev = None
- self.os_environment = dict(os.environ)
-
- def __str__(self):
- return "StateMachineStartItem with name %s (time: %s)" % (self.sm_dict['root_state_storage_id'], self.timestamp)
-
- def to_dict(self):
- record = HistoryItem.to_dict(self)
- record.update(self.sm_dict)
- record['call_type'] = 'EXECUTE'
- record['state_name'] = 'StateMachineStartItem'
- record['state_type'] = 'StateMachine'
- record['path'] = ''
- record['path_by_name'] = ''
- record['os_environment'] = self.os_environment
- if self.prev is not None:
- record['prev_history_item_id'] = self.prev.history_item_id
- else:
- record['prev_history_item_id'] = None
- return record
-
-
-class ScopedDataItem(HistoryItem):
- """A abstract class to represent history items which contains the scoped data of a state
-
- :ivar call_type: the call type of the execution step, i.e. if it refers to a container state or an execution state
- :ivar state_for_scoped_data: the state of which the scoped data will be stored as the context data that is necessary
- to re-execute the state
- """
-
- def __init__(self, state, prev, call_type, state_for_scoped_data, child_state_input_output_data, run_id):
- HistoryItem.__init__(self, state, prev, run_id)
- if call_type in CallType:
- self.call_type_str = call_type.name
- else:
- raise Exception('unkown calltype, neither CONTAINER nor EXECUTE')
- self.call_type = call_type
- self.scoped_data = {} if state_for_scoped_data is None else copy.deepcopy(state_for_scoped_data._scoped_data)
- self.child_state_input_output_data = copy.deepcopy(child_state_input_output_data)
-
- def to_dict(self):
- record = HistoryItem.to_dict(self)
- scoped_data_dict = {}
- for k, v in self.scoped_data.items():
- try:
- scoped_data_dict[v.name] = pickle.dumps(v.value)
- except Exception as e:
- scoped_data_dict['!' + v.name] = (str(e), str(v.value))
- # logger.debug('TypeError: Could not serialize one of the scoped data port types.')
- # record['scoped_data'] = json.dumps({'error_type': 'TypeError',
- # 'error_message': e}, cls=JSONObjectEncoder)
- record['scoped_data'] = scoped_data_dict
-
- child_state_input_output_dict = {}
- for k, v in self.child_state_input_output_data.items():
- try:
- child_state_input_output_dict[k] = pickle.dumps(v)
- except Exception as e:
- child_state_input_output_dict['!' + k] = (str(e), str(v))
- record['input_output_data'] = child_state_input_output_dict
-
- # from rafcon.core.states.container_state import ContainerState
- # if isinstance(self.state_reference, ContainerState):
- # try:
- # record['scoped_variables'] = json.dumps(self.state_reference.scoped_variables, cls=JSONObjectEncoder)
- # except TypeError as e:
- # # logger.exception('TypeError: Could not serialize one of the scoped variables types.')
- # # record['scoped_variables'] = json.dumps({'error_type': 'TypeError',
- # # 'error_message': e}, cls=JSONObjectEncoder)
- # record['scoped_variables'] = json.dumps(
- # {"key_all": {"name": "all_scoped_variables_as_string",
- # "value": str(self.state_reference.scoped_variables)}}, cls=JSONObjectEncoder
- # )
- # else:
- # record['scoped_variables'] = json.dumps({})
-
- record['call_type'] = self.call_type_str
- return record
-
- def __str__(self):
- return "SingleItem %s" % (HistoryItem.__str__(self))
-
-
-class CallItem(ScopedDataItem):
- """A history item to represent a state call
- """
- def __init__(self, state, prev, call_type, state_for_scoped_data, input_data, run_id):
- ScopedDataItem.__init__(self, state, prev, call_type, state_for_scoped_data, input_data, run_id)
- self.outcome = None
-
- def __str__(self):
- return "CallItem %s" % (ScopedDataItem.__str__(self))
-
- def to_dict(self):
- record = ScopedDataItem.to_dict(self)
- return record
-
-
-class ReturnItem(ScopedDataItem):
- """A history item to represent the return of a root state call
- """
- def __init__(self, state, prev, call_type, state_for_scoped_data, output_data, run_id):
- ScopedDataItem.__init__(self, state, prev, call_type, state_for_scoped_data, output_data, run_id)
- self.outcome = copy.deepcopy(state.final_outcome)
-
- def __str__(self):
- return "ReturnItem %s" % (ScopedDataItem.__str__(self))
-
- def to_dict(self):
- record = ScopedDataItem.to_dict(self)
- if self.outcome is not None:
- record['outcome_name'] = self.outcome.to_dict()['name']
- record['outcome_id'] = self.outcome.to_dict()['outcome_id']
- else:
- record['outcome_name'] = 'None'
- record['outcome_id'] = -1
- return record
-
-
-class ConcurrencyItem(HistoryItem):
- """A class to hold all the data for an invocation of several concurrent threads.
- """
- def __init__(self, container_state, prev, number_concurrent_threads, run_id, execution_history_storage):
- HistoryItem.__init__(self, container_state, prev, run_id)
- self.execution_histories = []
-
- for i in range(number_concurrent_threads):
- execution_history = ExecutionHistory(initial_prev=self)
- execution_history.set_execution_history_storage(execution_history_storage)
- self.execution_histories.append(execution_history)
-
- def __str__(self):
- return "ConcurrencyItem %s" % (HistoryItem.__str__(self))
-
- def to_dict(self):
- record = HistoryItem.to_dict(self)
- record['call_type'] = 'CONTAINER'
- return record
-
- def destroy(self):
- for execution_history in self.execution_histories:
- execution_history.destroy()
- super(ConcurrencyItem, self).destroy()
-
-
-CallType = Enum('METHOD_NAME', 'EXECUTE CONTAINER')
diff --git a/source/rafcon/core/execution/execution_history_factory.py b/source/rafcon/core/execution/execution_history_factory.py
new file mode 100644
index 000000000..3da72075c
--- /dev/null
+++ b/source/rafcon/core/execution/execution_history_factory.py
@@ -0,0 +1,35 @@
+
+from rafcon.core.config import global_config
+
+from rafcon.core.execution.in_memory_execution_history import InMemoryExecutionHistory
+from rafcon.core.execution.base_execution_history import BaseExecutionHistory
+
+from rafcon.utils import log
+logger = log.get_logger(__name__)
+
+
+class ExecutionHistoryFactory(object):
+ """ A factory class for creating an instance of BaseExecutionHistory or InMemoryExecutionHistory
+ """
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def get_execution_history(initial_prev=None, root_state_name="", consumer_manager=None):
+ """ Create an instance of a InMemoryExecutionHistory or BaseExecutionHistory
+
+ :param initial_prev: the initial previous history item
+ :param root_state_name: the root state name
+ :param consumer_manager: the consumer manager
+
+ :return: an instance of BaseExecutionHistory or InMemoryExecutionHistory
+ """
+ if global_config.get_config_value("IN_MEMORY_EXECUTION_HISTORY_ENABLE", True):
+ return InMemoryExecutionHistory(initial_prev=initial_prev,
+ root_state_name=root_state_name,
+ consumer_manager=consumer_manager)
+ else:
+ return BaseExecutionHistory(initial_prev=initial_prev,
+ root_state_name=root_state_name,
+ consumer_manager=consumer_manager)
diff --git a/source/rafcon/core/execution/execution_history_items.py b/source/rafcon/core/execution/execution_history_items.py
new file mode 100644
index 000000000..2b6093267
--- /dev/null
+++ b/source/rafcon/core/execution/execution_history_items.py
@@ -0,0 +1,266 @@
+import copy
+import json
+import os
+import pickle
+import time
+from enum import Enum
+
+from rafcon.core.id_generator import history_item_id_generator
+from rafcon.utils import log
+logger = log.get_logger(__name__)
+
+
+class HistoryItem(object):
+ """Class representing an entry within the history
+
+ An abstract class that serves as a data structure to hold all important information of a certain point in time
+ during the execution of a state machine. A history item is an element in a doubly linked history item list.
+
+ :ivar state_reference: a reference to the state performing a certain action that is going to be saved
+ :ivar path: the state path
+ :ivar timestamp: the time of the call/return
+ :ivar prev: the previous history item
+ :ivar next: the next history item
+ """
+
+ def __init__(self, state, run_id):
+ self._state_reference = state
+ self.path = copy.deepcopy(state.get_path())
+ self.timestamp = time.time()
+ self.run_id = run_id
+ self._prev = None
+ self._next = None
+ self.history_item_id = history_item_id_generator()
+ self.state_type = str(type(state).__name__)
+
+ def destroy(self):
+ self._state_reference = None
+ self.path = None
+ self.timestamp = None
+ self.run_id = None
+ self._prev = None
+ self._next = None
+ self.history_item_id = None
+ self.state_type = None
+
+ @property
+ def prev(self):
+ """Property for the prev field
+ """
+ return self._prev
+
+ @prev.setter
+ def prev(self, prev):
+ if not isinstance(prev, HistoryItem) and (prev is not None):
+ raise TypeError("prev must be of type HistoryItem")
+ self._prev = prev
+
+ @property
+ def next(self):
+ """Property for the next field
+ """
+ return self._next
+
+ @next.setter
+ def next(self, next):
+ if not isinstance(next, HistoryItem) and (next is not None):
+ raise TypeError("next must be of type HistoryItem")
+ self._next = next
+
+ @property
+ def state_reference(self):
+ """Property for the state_reference field
+ """
+ return self._state_reference
+
+ def __str__(self):
+ return "HistoryItem with reference state name %s (time: %s)" % (self.state_reference.name, self.timestamp)
+
+ def to_dict(self, pickled=True):
+ record = dict()
+
+ # here always the correct path is desired
+ record['path'] = self.state_reference.get_path()
+ record['path_by_name'] = self.state_reference.get_path(by_name=True)
+ record['state_type'] = str(type(self.state_reference).__name__)
+
+ from rafcon.core.states.library_state import LibraryState # delayed imported on purpose
+ if isinstance(self.state_reference, LibraryState):
+ # in case of a Library State, all the data of the library itself should be used
+ record['is_library'] = True
+ # TODO: rename key to library_root_state_name? However, this will break many analysis scripts => next minor
+ record['library_state_name'] = self.state_reference.state_copy.name
+ record['library_name'] = self.state_reference.library_name
+ record['library_path'] = self.state_reference.library_path
+ target_state = self.state_reference.state_copy
+ else:
+ record['is_library'] = False
+ record['library_state_name'] = None
+ record['library_name'] = None
+ record['library_path'] = None
+ target_state = self.state_reference
+
+ # there are 3 names of interest:
+ # self.state_reference.library_name (<- name of the library on the filesystem)
+ # self.state_reference.name (<- name of the user when using a library and changing the name)
+ # self.state_reference.state_copy.name (<- the name of the library root state)
+ record['state_name'] = self.state_reference.name
+ record['timestamp'] = self.timestamp
+ record['run_id'] = self.run_id # library state and state copy have the same run_id
+ record['history_item_id'] = self.history_item_id
+
+ # semantic data
+ semantic_data_dict = {}
+ for k, v in target_state.semantic_data.items():
+ try:
+ semantic_data_dict[k] = pickle.dumps(v) if pickled else json.dumps(v)
+ except Exception as e:
+ semantic_data_dict['!' + k] = (str(e), str(v))
+ record['semantic_data'] = semantic_data_dict
+
+ record['description'] = target_state.description
+
+ if self.prev is not None:
+ record['prev_history_item_id'] = self.prev.history_item_id
+ else:
+ record['prev_history_item_id'] = None
+ # store the specialized class name as item_type,
+ # e.g. CallItem, ReturnItem, StatemachineStartItem when saved
+ record['item_type'] = self.__class__.__name__
+ return record
+
+
+class StateMachineStartItem(HistoryItem):
+ def __init__(self, state_machine, run_id):
+ HistoryItem.__init__(self, state_machine.root_state, run_id)
+ from rafcon.core.state_machine import StateMachine
+ self.sm_dict = StateMachine.state_machine_to_dict(state_machine)
+ self.prev = None
+ self.os_environment = dict(os.environ)
+
+ def __str__(self):
+ return "StateMachineStartItem with name %s (time: %s)" % (self.sm_dict['root_state_storage_id'], self.timestamp)
+
+ def to_dict(self, pickled=True):
+ record = HistoryItem.to_dict(self, pickled=pickled)
+ record.update(self.sm_dict)
+ record['call_type'] = 'EXECUTE'
+ record['state_name'] = 'StateMachineStartItem'
+ record['state_type'] = 'StateMachine'
+ record['path'] = ''
+ record['path_by_name'] = ''
+ record['os_environment'] = self.os_environment
+ if self.prev is not None:
+ record['prev_history_item_id'] = self.prev.history_item_id
+ else:
+ record['prev_history_item_id'] = None
+ return record
+
+
+class ScopedDataItem(HistoryItem):
+ """A abstract class to represent history items which contains the scoped data of a state
+
+ :ivar call_type: the call type of the execution step, i.e. if it refers to a container state or an execution state
+ :ivar state_for_scoped_data: the state of which the scoped data will be stored as the context data that is necessary
+ to re-execute the state
+ """
+
+ def __init__(self, state, call_type, state_for_scoped_data, child_state_input_output_data, run_id):
+ HistoryItem.__init__(self, state, run_id)
+ if call_type in CallType:
+ self.call_type_str = call_type.name
+ else:
+ raise Exception('unkown calltype, neither CONTAINER nor EXECUTE')
+ self.call_type = call_type
+ self.scoped_data = {} if state_for_scoped_data is None else copy.deepcopy(state_for_scoped_data._scoped_data)
+ self.child_state_input_output_data = copy.deepcopy(child_state_input_output_data)
+
+ def to_dict(self, pickled=True):
+ record = HistoryItem.to_dict(self, pickled=pickled)
+ scoped_data_dict = {}
+ for k, v in self.scoped_data.items():
+ try:
+ scoped_data_dict[v.name] = pickle.dumps(v.value) if pickled else json.dumps(v.value)
+ except Exception as e:
+ scoped_data_dict['!' + v.name] = (str(e), str(v.value))
+ record['scoped_data'] = scoped_data_dict
+
+ child_state_input_output_dict = {}
+ for k, v in self.child_state_input_output_data.items():
+ try:
+ child_state_input_output_dict[k] = pickle.dumps(v) if pickled else json.dumps(v)
+ except Exception as e:
+ child_state_input_output_dict['!' + k] = (str(e), str(v))
+ record['input_output_data'] = child_state_input_output_dict
+ record['call_type'] = self.call_type_str
+ return record
+
+ def __str__(self):
+ return "SingleItem %s" % (HistoryItem.__str__(self))
+
+
+class CallItem(ScopedDataItem):
+ """A history item to represent a state call
+ """
+ def __init__(self, state, call_type, state_for_scoped_data, input_data, run_id):
+ ScopedDataItem.__init__(self, state, call_type, state_for_scoped_data, input_data, run_id)
+ self.outcome = None
+
+ def __str__(self):
+ return "CallItem %s" % (ScopedDataItem.__str__(self))
+
+ def to_dict(self, pickled=True):
+ record = ScopedDataItem.to_dict(self, pickled=pickled)
+ return record
+
+
+class ReturnItem(ScopedDataItem):
+ """A history item to represent the return of a root state call
+ """
+ def __init__(self, state, call_type, state_for_scoped_data, output_data, run_id):
+ ScopedDataItem.__init__(self, state, call_type, state_for_scoped_data, output_data, run_id)
+ self.outcome = copy.deepcopy(state.final_outcome)
+
+ def __str__(self):
+ return "ReturnItem %s" % (ScopedDataItem.__str__(self))
+
+ def to_dict(self, pickled=True):
+ record = ScopedDataItem.to_dict(self, pickled=pickled)
+ if self.outcome is not None:
+ record['outcome_name'] = self.outcome.to_dict()['name']
+ record['outcome_id'] = self.outcome.to_dict()['outcome_id']
+ else:
+ record['outcome_name'] = 'None'
+ record['outcome_id'] = -1
+ return record
+
+
+class ConcurrencyItem(HistoryItem):
+ """A class to hold all the data for an invocation of several concurrent threads.
+ """
+ def __init__(self, container_state, number_concurrent_threads, run_id, consumer_manager):
+ HistoryItem.__init__(self, container_state, run_id)
+ self.execution_histories = []
+
+ from rafcon.core.execution.in_memory_execution_history import InMemoryExecutionHistory
+ from rafcon.core.execution.execution_history_factory import ExecutionHistoryFactory
+ for i in range(number_concurrent_threads):
+ execution_history = ExecutionHistoryFactory.get_execution_history(initial_prev=self,
+ consumer_manager=consumer_manager)
+ self.execution_histories.append(execution_history)
+
+ def __str__(self):
+ return "ConcurrencyItem %s" % (HistoryItem.__str__(self))
+
+ def to_dict(self, pickled=True):
+ record = HistoryItem.to_dict(self, pickled=pickled)
+ record['call_type'] = 'CONTAINER'
+ return record
+
+ def destroy(self):
+ for execution_history in self.execution_histories:
+ execution_history.destroy()
+ super(ConcurrencyItem, self).destroy()
+
+
+CallType = Enum('METHOD_NAME', 'EXECUTE CONTAINER')
\ No newline at end of file
diff --git a/source/rafcon/core/execution/execution_status.py b/source/rafcon/core/execution/execution_status.py
index bcc65b95a..d21b96eed 100644
--- a/source/rafcon/core/execution/execution_status.py
+++ b/source/rafcon/core/execution/execution_status.py
@@ -89,6 +89,7 @@ def execution_mode(self, execution_mode):
self._execution_mode = execution_mode
-StateMachineExecutionStatus = Enum('STATE_MACHINE_EXECUTION_STATUS', 'STARTED STOPPED PAUSED FINISHED '
- 'STEP_MODE FORWARD_INTO FORWARD_OVER FORWARD_OUT '
- 'BACKWARD RUN_TO_SELECTED_STATE')
+StateMachineExecutionStatus = Enum('STATE_MACHINE_EXECUTION_STATUS',
+ 'STARTED STOPPED PAUSED FINISHED '
+ 'STEP_MODE FORWARD_INTO FORWARD_OVER FORWARD_OUT '
+ 'BACKWARD RUN_TO_SELECTED_STATE RUN_SELECTED_STATE')
diff --git a/source/rafcon/core/execution/in_memory_execution_history.py b/source/rafcon/core/execution/in_memory_execution_history.py
new file mode 100644
index 000000000..b50c51edc
--- /dev/null
+++ b/source/rafcon/core/execution/in_memory_execution_history.py
@@ -0,0 +1,198 @@
+# Copyright (C) 2014-2018 DLR
+#
+# All rights reserved. This program and the accompanying materials are made
+# available under the terms of the Eclipse Public License v1.0 which
+# accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Franz Steinmetz
+# Rico Belder
+# Sebastian Brunner
+# Sebastian Riedel
+# ried_sa
+
+"""
+.. module:: execution_history
+ :synopsis: A module for the history of one thread during state machine execution
+
+"""
+from collections import Iterable, Sized
+
+from gtkmvc3.observable import Observable
+
+from rafcon.core.execution.base_execution_history import BaseExecutionHistory
+from rafcon.utils import log
+logger = log.get_logger(__name__)
+
+
+class InMemoryExecutionHistory(BaseExecutionHistory, Observable, Iterable, Sized):
+ """A class for the history of a state machine execution
+
+ It stores all history elements in a stack wise fashion.
+
+ :ivar initial_prev: optional link to a previous element for the first element pushed into this history of
+ type :class:`rafcon.core.execution.execution_history.HistoryItem`
+ """
+
+ def __init__(self, initial_prev=None, root_state_name="", consumer_manager=None):
+ BaseExecutionHistory.__init__(self, initial_prev=initial_prev,
+ root_state_name=root_state_name,
+ consumer_manager=consumer_manager)
+ Observable.__init__(self)
+ Iterable.__init__(self)
+ Sized.__init__(self)
+ self._history_items = []
+ logger.debug("InMemoryExecutionHistory has been created")
+
+ def destroy(self):
+ """Destroy the consumer and reset all variables
+ """
+ logger.verbose("Destroy execution history!")
+ super(InMemoryExecutionHistory, self).destroy()
+ if len(self._history_items) > 0:
+ if self._history_items[0]:
+ execution_history_iterator = iter(self)
+ for history_item in execution_history_iterator:
+ history_item.destroy()
+ self.destroyed = True
+ self._history_items = None
+ self.initial_prev = None
+
+ def shutdown(self):
+ """Stop all consumers including consumer plugins
+ """
+ self.consumer_manager.stop_consumers()
+
+ def __iter__(self):
+ return iter(self._history_items)
+
+ def __len__(self):
+ return len(self._history_items)
+
+ def __getitem__(self, index):
+ return self._history_items[index]
+
+ def get_last_history_item(self):
+ """Returns the history item that was added last
+
+ :return: History item added last
+ :rtype: rafcon.core.execution.execution_history_items.HistoryItem
+ """
+ try:
+ return self[-1]
+ except IndexError: # this is the case for the very first executed state
+ return None
+
+ def _push_item(self, current_item):
+ """Push history item to the queue
+
+ :param current_item: the history item
+
+ :return: History item added
+ :rtype: rafcon.core.execution.execution_history_items.HistoryItem
+ """
+ try:
+ self._history_items.append(current_item)
+ except AttributeError:
+ if self.destroyed:
+ pass # this is fine
+ else:
+ raise
+ return current_item
+
+ def _link_history_item(self, current_item):
+ """Link the history item to the previous one
+
+ :param current_item: the history item
+ """
+ last_history_item = self.get_last_history_item()
+ if last_history_item is None:
+ current_item.prev = self.initial_prev
+ if last_history_item is not None:
+ current_item.prev = last_history_item
+ last_history_item.next = current_item
+
+ @Observable.observed
+ def push_call_history_item(self, state, call_type, state_for_scoped_data, input_data=None):
+ """Adds a new call-history-item to the history item list
+
+ A call history items stores information about the point in time where a method (entry, execute,
+ exit) of certain state was called.
+
+ :param state: the state that was called
+ :param call_type: the call type of the execution step,
+ i.e. if it refers to a container state or an execution state
+ :param state_for_scoped_data: the state of which the scoped data needs to be saved for further usages
+ (e.g. backward stepping)
+ """
+ return_item = super(InMemoryExecutionHistory, self).push_call_history_item(
+ state, call_type, state_for_scoped_data, input_data, link_and_feed_item_to_consumers=False)
+ self._link_history_item(return_item)
+ super(InMemoryExecutionHistory, self).feed_consumers(return_item)
+ return self._push_item(return_item)
+
+ @Observable.observed
+ def push_return_history_item(self, state, call_type, state_for_scoped_data, output_data=None):
+ """Adds a new return-history-item to the history item list
+
+ A return history items stores information about the point in time where a method (entry, execute,
+ exit) of certain state returned.
+
+ :param state: the state that returned
+ :param call_type: the call type of the execution step,
+ i.e. if it refers to a container state or an execution state
+ :param state_for_scoped_data: the state of which the scoped data needs to be saved for further usages (e.g.
+ backward stepping)
+ """
+ return_item = super(InMemoryExecutionHistory, self).push_return_history_item(
+ state, call_type, state_for_scoped_data, output_data, link_and_feed_item_to_consumers=False)
+ self._link_history_item(return_item)
+ super(InMemoryExecutionHistory, self).feed_consumers(return_item)
+ return self._push_item(return_item)
+
+ @Observable.observed
+ def push_concurrency_history_item(self, state, number_concurrent_threads):
+ """Adds a new concurrency-history-item to the history item list
+
+ A concurrent history item stores information about the point in time where a certain number of states is
+ launched concurrently
+ (e.g. in a barrier concurrency state).
+
+ :param state: the state that launches the state group
+ :param number_concurrent_threads: the number of states that are launched
+ """
+ return_item = super(InMemoryExecutionHistory, self).push_concurrency_history_item(
+ state, number_concurrent_threads, link_and_feed_item_to_consumers=False)
+ self._link_history_item(return_item)
+ super(InMemoryExecutionHistory, self).feed_consumers(return_item)
+ return self._push_item(return_item)
+
+ @Observable.observed
+ def push_state_machine_start_history_item(self, state_machine, run_id):
+ """Adds a new state-machine-start-history-item to the history item list
+
+ A state machine start history item stores information about the point in time where a state machine
+ started to run
+
+ :param state_machine: the state machine that started
+ :param run_id: the run id
+ """
+ return_item = super(InMemoryExecutionHistory, self).push_state_machine_start_history_item(
+ state_machine, run_id, feed_item_to_consumers=False)
+ self._history_items.append(return_item)
+ super(InMemoryExecutionHistory, self).feed_consumers(return_item)
+ return return_item
+
+ @Observable.observed
+ def pop_last_item(self):
+ """Delete and returns the last item of the history item list.
+
+ :return: History item added last
+ :rtype: rafcon.core.execution.execution_history_items.HistoryItem
+ """
+ try:
+ return self._history_items.pop()
+ except IndexError:
+ logger.error("No item left in the history item list in the execution history.")
+ return None
diff --git a/source/rafcon/core/library_manager.py b/source/rafcon/core/library_manager.py
index fe2fb06c0..fbb102447 100644
--- a/source/rafcon/core/library_manager.py
+++ b/source/rafcon/core/library_manager.py
@@ -63,6 +63,16 @@ def __init__(self):
self._loaded_libraries = {}
self._libraries_instances = {}
+ self._show_dialog = True
+
+ @property
+ def show_dialog(self):
+ return self._show_dialog
+
+ @show_dialog.setter
+ def show_dialog(self, value):
+ self._show_dialog = value
+
def prepare_destruction(self):
self.clean_loaded_libraries()
@@ -262,7 +272,7 @@ def get_os_path_to_library(self, library_path, library_name, allow_user_interact
regularly_found = False
new_library_os_path = None
- if allow_user_interaction:
+ if allow_user_interaction and self.show_dialog:
notice = "Cannot find library '{0}' in library_path '{1}' in any of the library root paths. " \
"Please check your library root paths configuration in config.yaml " \
"LIBRARY_PATHS and environment variable RAFCON_LIBRARY_PATH. " \
diff --git a/source/rafcon/core/state_elements/data_port.py b/source/rafcon/core/state_elements/data_port.py
index 043138be4..ebd453cf3 100644
--- a/source/rafcon/core/state_elements/data_port.py
+++ b/source/rafcon/core/state_elements/data_port.py
@@ -58,6 +58,8 @@ def __init__(self, name=None, data_type=None, default_value=None, data_port_id=N
raise NotImplementedError
super(DataPort, self).__init__(safe_init=safe_init)
self._no_type_error_exceptions = True if init_without_default_value_type_exceptions else False
+ if global_config.get_config_value("LIBRARY_RECOVERY_MODE") is True:
+ self._no_type_error_exceptions = True
self._was_forced_type = force_type
if data_port_id is None:
self._data_port_id = generate_data_port_id([])
@@ -66,8 +68,6 @@ def __init__(self, name=None, data_type=None, default_value=None, data_port_id=N
else:
self._data_port_id = data_port_id
- self._no_type_error_exceptions = False
-
if safe_init:
DataPort._safe_init(self, name, data_type, default_value, parent)
else:
@@ -266,7 +266,7 @@ def check_default_value(self, default_value, data_type=None):
else:
if not isinstance(default_value, self.data_type):
if self._no_type_error_exceptions:
- logger.warning("Handed default value '{0}' is of type '{1}' but data port data type is {2} {3}."
+ logger.error("Handed default value '{0}' is of type '{1}' but data port data type is {2} {3}."
"".format(default_value, type(default_value), data_type, self))
else:
raise TypeError("Handed default value '{0}' is of type '{1}' but data port data type is {2}"
diff --git a/source/rafcon/core/state_machine.py b/source/rafcon/core/state_machine.py
index e880e5898..8a824cf18 100644
--- a/source/rafcon/core/state_machine.py
+++ b/source/rafcon/core/state_machine.py
@@ -33,16 +33,14 @@
from jsonconversion.jsonobject import JSONObject
import rafcon
-from rafcon.core.execution.execution_history import ExecutionHistory, ExecutionHistoryStorage
+from rafcon.core.execution.execution_history_factory import ExecutionHistoryFactory
from rafcon.core.id_generator import generate_state_machine_id, run_id_generator
from rafcon.utils import log
from rafcon.utils.hashable import Hashable
from rafcon.utils.storage_utils import get_current_time_string
import time
-from rafcon.utils.constants import RAFCON_TEMP_PATH_BASE
from rafcon.core.config import global_config
-import os
logger = log.get_logger(__name__)
@@ -142,18 +140,25 @@ def start(self):
self._root_state.input_data = self._root_state.get_default_input_values_for_state(self._root_state)
self._root_state.output_data = self._root_state.create_output_dictionary_for_state(self._root_state)
new_execution_history = self._add_new_execution_history()
- if new_execution_history is not None:
- new_execution_history.push_state_machine_start_history_item(self, run_id_generator())
+ new_execution_history.push_state_machine_start_history_item(self, run_id_generator())
self._root_state.start(new_execution_history)
def join(self):
"""Wait for root state to finish execution"""
+ from rafcon.core.states.concurrency_state import ConcurrencyState
self._root_state.join()
- # execution finished, close execution history log file (if present)
- if len(self._execution_histories) > 0:
- if self._execution_histories[-1].execution_history_storage is not None:
- set_read_and_writable_for_all = global_config.get_config_value("EXECUTION_LOG_SET_READ_AND_WRITABLE_FOR_ALL", False)
- self._execution_histories[-1].execution_history_storage.close(set_read_and_writable_for_all)
+ if not global_config.get_config_value("IN_MEMORY_EXECUTION_HISTORY_ENABLE", False):
+ queue = [self.root_state]
+ while len(queue) > 0:
+ state = queue.pop(0)
+ if isinstance(state, ConcurrencyState):
+ if state.concurrency_history_item is not None:
+ state.concurrency_history_item.destroy()
+ state.concurrency_history_item = None
+ elif hasattr(state, 'states'):
+ queue.extend(state.states.values())
+ if len(self.execution_histories) > 0:
+ self.execution_histories[-1].shutdown()
from rafcon.core.states.state import StateExecutionStatus
self._root_state.state_execution_status = StateExecutionStatus.INACTIVE
@@ -226,24 +231,7 @@ def destroy_execution_histories(self):
@Observable.observed
def _add_new_execution_history(self):
-
- if not global_config.get_config_value("EXECUTION_HISTORY_ENABLE", True):
- logger.debug("Execution history logging has been disabled")
- return None
-
- new_execution_history = ExecutionHistory()
-
- if global_config.get_config_value("EXECUTION_LOG_ENABLE", False):
- base_dir = global_config.get_config_value("EXECUTION_LOG_PATH", "%RAFCON_TEMP_PATH_BASE/execution_logs")
- if base_dir.startswith('%RAFCON_TEMP_PATH_BASE'):
- base_dir = base_dir.replace('%RAFCON_TEMP_PATH_BASE', RAFCON_TEMP_PATH_BASE)
- if not os.path.exists(base_dir):
- os.makedirs(base_dir)
- shelve_name = os.path.join(base_dir, '%s_rafcon_execution_log_%s.shelve' %
- (time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime()),
- self.root_state.name.replace(' ', '-')))
- execution_history_store = ExecutionHistoryStorage(shelve_name)
- new_execution_history.set_execution_history_storage(execution_history_store)
+ new_execution_history = ExecutionHistoryFactory.get_execution_history(root_state_name=self.root_state.name)
self._execution_histories.append(new_execution_history)
return new_execution_history
@@ -311,8 +299,8 @@ def get_state_by_path(self, path, as_check=False):
def get_last_execution_log_filename(self):
if len(self._execution_histories) > 0:
for i in range(len(self._execution_histories) - 1, -1, -1):
- if self._execution_histories[i].execution_history_storage is not None:
- return self._execution_histories[i].execution_history_storage.filename
+ if self._execution_histories[i].consumer_manager.get_file_system_consumer_file_name() is not None:
+ return self._execution_histories[i].consumer_manager.get_file_system_consumer_file_name()
return None
else:
return None
diff --git a/source/rafcon/core/state_machine_manager.py b/source/rafcon/core/state_machine_manager.py
index d00849f9b..f8fdf1716 100644
--- a/source/rafcon/core/state_machine_manager.py
+++ b/source/rafcon/core/state_machine_manager.py
@@ -125,6 +125,19 @@ def remove_state_machine(self, state_machine_id):
removed_state_machine.destroy_execution_histories()
return removed_state_machine
+ def remove_state_machine_by_path(self, state_machine_path):
+ """ Remove an open state machine by path
+
+ :param str state_machine_path: the state machine path
+ """
+
+ state_machine_ids = []
+ for state_machine_id, state_machine in self._state_machines.items():
+ if state_machine.file_system_path == state_machine_path:
+ state_machine_ids.append(state_machine_id)
+ for state_machine_id in state_machine_ids:
+ self.remove_state_machine(state_machine_id)
+
def get_active_state_machine(self):
"""Return a reference to the active state-machine
"""
diff --git a/source/rafcon/core/states/concurrency_state.py b/source/rafcon/core/states/concurrency_state.py
index e6dde3c2a..254c658de 100644
--- a/source/rafcon/core/states/concurrency_state.py
+++ b/source/rafcon/core/states/concurrency_state.py
@@ -22,9 +22,9 @@
from gtkmvc3.observable import Observable
import rafcon.core.singleton as singleton
+from rafcon.core.config import global_config
from rafcon.core.states.container_state import ContainerState
-from rafcon.core.execution.execution_history import CallType
-from rafcon.core.execution.execution_history import CallItem, ReturnItem, ConcurrencyItem
+from rafcon.core.execution.execution_history_items import CallItem, ReturnItem, ConcurrencyItem, CallType
from rafcon.core.states.state import StateExecutionStatus
from rafcon.core.state_elements.logical_port import Outcome
@@ -40,6 +40,7 @@ def __init__(self, name=None, state_id=None, input_keys=None, output_keys=None,
scoped_variables=None, safe_init=True):
ContainerState.__init__(self, name, state_id, input_keys, output_keys, income, outcomes, states, transitions,
data_flows, start_state_id, scoped_variables, safe_init=safe_init)
+ self.concurrency_history_item = None
def run(self, *args, **kwargs):
""" The abstract run method that has to be implemented by all concurrency states.
@@ -73,11 +74,11 @@ def setup_forward_or_backward_execution(self):
assert isinstance(concurrency_history_item, ConcurrencyItem)
else: # forward_execution
- if self.execution_history is not None:
- self.execution_history.push_call_history_item(self, CallType.CONTAINER, self, self.input_data)
- concurrency_history_item = self.execution_history.push_concurrency_history_item(self, len(self.states))
- else:
- return None
+ self.execution_history.push_call_history_item(self, CallType.CONTAINER, self, self.input_data)
+ concurrency_history_item = self.execution_history.push_concurrency_history_item(self, len(self.states))
+ # Save a reference to the concurrency_history_item here in order to be able to destruct it
+ # after the concurrency_state execution has finished
+ self.concurrency_history_item = concurrency_history_item
return concurrency_history_item
def start_child_states(self, concurrency_history_item, do_not_start_state=None):
@@ -173,8 +174,7 @@ def finalize_concurrency_state(self, outcome):
final_outcome = outcome
self.write_output_data()
self.check_output_data_type()
- if self.execution_history is not None:
- self.execution_history.push_return_history_item(self, CallType.CONTAINER, self, self.output_data)
+ self.execution_history.push_return_history_item(self, CallType.CONTAINER, self, self.output_data)
self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE
singleton.state_machine_execution_engine._modify_run_to_states(self)
diff --git a/source/rafcon/core/states/container_state.py b/source/rafcon/core/states/container_state.py
index f2422b529..1237e19ee 100644
--- a/source/rafcon/core/states/container_state.py
+++ b/source/rafcon/core/states/container_state.py
@@ -66,7 +66,7 @@ class ContainerState(State):
def __init__(self, name=None, state_id=None, input_data_ports=None, output_data_ports=None,
income=None, outcomes=None,
states=None, transitions=None, data_flows=None, start_state_id=None,
- scoped_variables=None, safe_init=True):
+ scoped_variables=None, missing_library_meta_data=None, is_dummy=False, safe_init=True):
self._states = OrderedDict()
self._transitions = {}
@@ -78,6 +78,17 @@ def __init__(self, name=None, state_id=None, input_data_ports=None, output_data_
self._transitions_cv = Condition()
self._child_execution = False
self._start_state_modified = False
+ """
+ Dummy state machine is created only in one place and it is a ContrainerState.
+ So, it is always a ContrainerState by design.
+ """
+ self._is_dummy = is_dummy
+ """
+ In the case of a dummy state machine, we must use the same meta_data as the missing library was using.
+ Hence, we must add an additional field for this case as it is not possible to handle it in a
+ container state model when a library does not exist anymore.
+ """
+ self._missing_library_meta_data = missing_library_meta_data
State.__init__(self, name, state_id, input_data_ports, output_data_ports, income, outcomes, safe_init=safe_init)
@@ -1031,22 +1042,25 @@ def substitute_state(self, state_id, state):
act_output_data_port_by_name = {op.name: op for op in state.output_data_ports.values()}
for t in related_transitions['external']['self']:
- new_t_id = self.add_transition(state_id, t.from_outcome, state_id, t.to_outcome, t.transition_id)
- re_create_io_going_t_ids.append(new_t_id)
- assert new_t_id == t.transition_id
+ if t.transition_id not in self._transitions:
+ new_t_id = self.add_transition(state_id, t.from_outcome, state_id, t.to_outcome, t.transition_id)
+ re_create_io_going_t_ids.append(new_t_id)
+ assert new_t_id == t.transition_id
for t in related_transitions['external']['ingoing']:
- new_t_id = self.add_transition(t.from_state, t.from_outcome, state_id, t.to_outcome, t.transition_id)
- re_create_io_going_t_ids.append(new_t_id)
- assert new_t_id == t.transition_id
-
- for t in related_transitions['external']['outgoing']:
- from_outcome = act_outcome_ids_by_name.get(old_outcome_names[t.from_outcome], None)
- if from_outcome is not None:
- new_t_id = self.add_transition(state_id, from_outcome, t.to_state, t.to_outcome, t.transition_id)
+ if t.transition_id not in self._transitions:
+ new_t_id = self.add_transition(t.from_state, t.from_outcome, state_id, t.to_outcome, t.transition_id)
re_create_io_going_t_ids.append(new_t_id)
assert new_t_id == t.transition_id
+ for t in related_transitions['external']['outgoing']:
+ if t.transition_id not in self._transitions:
+ from_outcome = act_outcome_ids_by_name.get(old_outcome_names[t.from_outcome], None)
+ if from_outcome is not None:
+ new_t_id = self.add_transition(state_id, from_outcome, t.to_state, t.to_outcome, t.transition_id)
+ re_create_io_going_t_ids.append(new_t_id)
+ assert new_t_id == t.transition_id
+
for old_ip in old_input_data_ports.values():
ip = act_input_data_port_by_name.get(old_input_data_ports[old_ip.data_port_id].name, None)
if ip is not None and ip.data_type == old_input_data_ports[old_ip.data_port_id].data_type:
@@ -1635,6 +1649,10 @@ def add_state_execution_output_to_scoped_data(self, dictionary, state):
not (isinstance(value, type(None)))):
logger.error("The data type of output port {0} should be of type {1}, but is of type {2}".
format(output_name, data_port.data_type, type(value)))
+ elif value is None:
+ logger.warning(
+ "The value of output port is 'None'. It has replaced with the default value.".
+ format(output_name, data_port.data_type, type(value)))
self.scoped_data[str(output_data_port_key) + state.state_id] = \
ScopedData(data_port.name, value, type(value), state.state_id, OutputDataPort, parent=self)
@@ -2239,7 +2257,8 @@ def transitions(self, transitions):
transition.parent = self
except (ValueError, RecoveryModeException) as e:
if type(e) is RecoveryModeException:
- logger.exception("Recovery error:")
+ logger.exception("Recovery error: " + str(e))
+ logger.error("The transition is going to be deleted!")
if e.do_delete_item:
transition_ids_to_delete.append(transition.transition_id)
else:
@@ -2296,7 +2315,8 @@ def data_flows(self, data_flows):
data_flow.parent = self
except (ValueError, RecoveryModeException) as e:
if type(e) is RecoveryModeException:
- logger.error("Recovery error:")
+ logger.error("Recovery error: " + str(e))
+ logger.error("The data-flow is going to be deleted!")
if e.do_delete_item:
data_flow_ids_to_delete.append(data_flow.data_flow_id)
else:
@@ -2436,3 +2456,27 @@ def child_execution(self):
return True
else:
return False
+
+ @property
+ def is_dummy(self):
+ """Property for the _is_dummy field
+ """
+
+ return self._is_dummy
+
+ @is_dummy.setter
+ @lock_state_machine
+ def is_dummy(self, is_dummy):
+ self._is_dummy = is_dummy
+
+ @property
+ def missing_library_meta_data(self):
+ """Property for the _missing_library_meta_data field
+ """
+
+ return self._missing_library_meta_data
+
+ @missing_library_meta_data.setter
+ @lock_state_machine
+ def missing_library_meta_data(self, missing_library_meta_data):
+ self._missing_library_meta_data = missing_library_meta_data
diff --git a/source/rafcon/core/states/execution_state.py b/source/rafcon/core/states/execution_state.py
index dff5dd553..02abe3057 100644
--- a/source/rafcon/core/states/execution_state.py
+++ b/source/rafcon/core/states/execution_state.py
@@ -31,7 +31,7 @@
from rafcon.core.state_elements.logical_port import Outcome
from rafcon.core.script import Script
from rafcon.core.states.state import StateExecutionStatus
-from rafcon.core.execution.execution_history import CallType
+from rafcon.core.execution.execution_history_items import CallType
from rafcon.core.config import global_config
from rafcon.utils import log
diff --git a/source/rafcon/core/states/hierarchy_state.py b/source/rafcon/core/states/hierarchy_state.py
index 70bbc12a5..aaf6bcf6f 100644
--- a/source/rafcon/core/states/hierarchy_state.py
+++ b/source/rafcon/core/states/hierarchy_state.py
@@ -24,7 +24,7 @@
from rafcon.core.states.container_state import ContainerState
from rafcon.core.state_elements.logical_port import Outcome
import rafcon.core.singleton as singleton
-from rafcon.core.execution.execution_history import CallItem, CallType, ReturnItem
+from rafcon.core.execution.execution_history_items import CallItem, ReturnItem, CallType
from rafcon.core.execution.execution_status import StateMachineExecutionStatus
from rafcon.core.states.state import StateExecutionStatus
@@ -42,10 +42,11 @@ class HierarchyState(ContainerState):
def __init__(self, name=None, state_id=None, input_data_ports=None, output_data_ports=None,
income=None, outcomes=None, states=None, transitions=None, data_flows=None, start_state_id=None,
- scoped_variables=None, safe_init=True):
+ scoped_variables=None, missing_library_meta_data=None, is_dummy=False, safe_init=True):
ContainerState.__init__(self, name, state_id, input_data_ports, output_data_ports, income, outcomes, states,
- transitions, data_flows, start_state_id, scoped_variables, safe_init=safe_init)
+ transitions, data_flows, start_state_id, scoped_variables, missing_library_meta_data,
+ is_dummy, safe_init=safe_init)
self.handling_execution_mode = False
self.child_state = None
@@ -72,15 +73,13 @@ def _initialize_hierarchy(self):
self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE
if self.backward_execution:
- # if-clause actually not needed as in backward execution case there always exist an execution_history
- if self.execution_history is not None:
- last_history_item = self.execution_history.pop_last_item()
+ # in backward execution case there always has an execution_history to exist
+ last_history_item = self.execution_history.pop_last_item()
assert isinstance(last_history_item, ReturnItem)
self.scoped_data = last_history_item.scoped_data
else: # forward_execution
- if self.execution_history is not None:
- self.execution_history.push_call_history_item(self, CallType.CONTAINER, self, self.input_data)
+ self.execution_history.push_call_history_item(self, CallType.CONTAINER, self, self.input_data)
self.child_state = self.get_start_state(set_final_outcome=True)
if self.child_state is None:
self.child_state = self.handle_no_start_state()
@@ -202,9 +201,8 @@ def _execute_current_child(self):
self.child_state.generate_run_id()
if not self.backward_execution: # only add history item if it is not a backward execution
- if self.execution_history is not None:
- self.execution_history.push_call_history_item(
- self.child_state, CallType.EXECUTE, self, self.child_state.input_data)
+ self.execution_history.push_call_history_item(
+ self.child_state, CallType.EXECUTE, self, self.child_state.input_data)
self.child_state.start(self.execution_history, backward_execution=self.backward_execution,
generate_run_id=False)
@@ -267,9 +265,8 @@ def _handle_forward_execution_after_child_execution(self):
"""
self.add_state_execution_output_to_scoped_data(self.child_state.output_data, self.child_state)
self.update_scoped_variables_with_output_dictionary(self.child_state.output_data, self.child_state)
- if self.execution_history is not None:
- self.execution_history.push_return_history_item(
- self.child_state, CallType.EXECUTE, self, self.child_state.output_data)
+ self.execution_history.push_return_history_item(
+ self.child_state, CallType.EXECUTE, self, self.child_state.output_data)
# not explicitly connected preempted outcomes are implicit connected to parent preempted outcome
transition = self.get_transition_for_outcome(self.child_state, self.child_state.final_outcome)
@@ -302,8 +299,7 @@ def _finalize_hierarchy(self):
self.output_data['error'] = copy.deepcopy(self.last_error)
self.write_output_data()
self.check_output_data_type()
- if self.execution_history is not None:
- self.execution_history.push_return_history_item(self, CallType.CONTAINER, self, self.output_data)
+ self.execution_history.push_return_history_item(self, CallType.CONTAINER, self, self.output_data)
# add error message from child_state to own output_data
self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE
diff --git a/source/rafcon/core/states/state.py b/source/rafcon/core/states/state.py
index 43c83d3e6..06639e8d1 100644
--- a/source/rafcon/core/states/state.py
+++ b/source/rafcon/core/states/state.py
@@ -352,11 +352,12 @@ def get_previously_executed_state(self):
:return: The last state in the execution history
"""
- if self.execution_history is not None:
- return self.execution_history.get_last_history_item().prev.state_reference
- else:
- logger.error("State does not have an execution history. "
- "This means that execution history logging is disabled.")
+ previous_state = self.execution_history.get_last_history_item().prev.state_reference
+ if not previous_state:
+ logger.warning(
+ "The In-Memory-Execution-History is disabled. "
+ "Thus, then previously executed state cannot be retrieved.")
+ return previous_state
# ---------------------------------------------------------------------------------------------
# ------------------------------- input/output data handling ----------------------------------
diff --git a/source/rafcon/core/storage/storage.py b/source/rafcon/core/storage/storage.py
index 642be6e2d..429d848c8 100644
--- a/source/rafcon/core/storage/storage.py
+++ b/source/rafcon/core/storage/storage.py
@@ -35,11 +35,14 @@
from rafcon.utils import storage_utils
from rafcon.utils import log
from rafcon.utils.timer import measure_time
+from rafcon.utils.vividict import Vividict
from rafcon.core.custom_exceptions import LibraryNotFoundException
from rafcon.core.constants import DEFAULT_SCRIPT_PATH
from rafcon.core.config import global_config
from rafcon.core.state_machine import StateMachine
+from rafcon.core.state_elements.logical_port import Outcome
+from rafcon.core.state_elements.data_port import InputDataPort, OutputDataPort
logger = log.get_logger(__name__)
@@ -71,7 +74,7 @@ def remove_obsolete_folders(states, path):
This function removes all folders in the file system folder `path` that do not belong to the states given by
`states`.
-
+
:param list states: the states that should reside in this very folder
:param str path: the file system path to be checked for valid folders
"""
@@ -134,7 +137,7 @@ def clean_path(base_path):
"""
This function cleans a file system path in terms of removing all not allowed characters of each path element.
A path element is an element of a path between the path separator of the operating system.
-
+
:param base_path: the path to be cleaned
:return: the clean path
"""
@@ -311,7 +314,7 @@ def load_state_machine_from_path(base_path, state_machine_id=None):
state_machine_dict['used_rafcon_version'], rafcon.__version__)
rafcon_older_than_sm_version = "You are trying to load a state machine that was stored with an newer " \
"version of RAFCON ({0}) than the one you are using ({1}).".format(
- state_machine_dict['used_rafcon_version'], rafcon.__version__)
+ state_machine_dict['used_rafcon_version'], rafcon.__version__)
note_about_possible_incompatibility = "The state machine will be loaded with no guarantee of success."
if active_rafcon_version[0] > previously_used_rafcon_version[0]:
@@ -350,6 +353,7 @@ def load_state_machine_from_path(base_path, state_machine_id=None):
dirty_states = []
state_machine.root_state = load_state_recursively(parent=state_machine, state_path=root_state_path,
dirty_states=dirty_states)
+ reconnect_data_flow(state_machine)
if state_machine.root_state is None:
return # a corresponding exception has been handled with a proper error log in load_state_recursively
if len(dirty_states) > 0:
@@ -369,6 +373,57 @@ def load_state_machine_from_path(base_path, state_machine_id=None):
return state_machine
+def reconnect_data_flow(state_machine):
+ queue = [state_machine.root_state]
+ while len(queue) > 0:
+ state = queue.pop(0)
+ if hasattr(state, 'is_dummy') and state.is_dummy:
+ same_level_states = [state.parent]
+ for same_level_state in state.parent.states.values():
+ if same_level_state.state_id != state.state_id:
+ same_level_states.append(same_level_state)
+ for same_level_state in same_level_states:
+ for data_flow in state.parent.data_flows.values():
+ data_type = int
+ default_value = None
+ if data_flow.from_state == state.state_id and data_flow.to_state == same_level_state.state_id:
+ if data_flow.to_key in same_level_state.input_data_ports:
+ data_type = same_level_state.input_data_ports[data_flow.to_key].data_type
+ default_value = same_level_state.input_data_ports[data_flow.to_key].default_value
+ elif data_flow.to_key in same_level_state.output_data_ports:
+ data_type = same_level_state.output_data_ports[data_flow.to_key].data_type
+ default_value = same_level_state.output_data_ports[data_flow.to_key].default_value
+ elif data_flow.to_key in same_level_state.scoped_variables:
+ data_type = same_level_state.scoped_variables[data_flow.to_key].data_type
+ default_value = same_level_state.scoped_variables[data_flow.to_key].default_value
+ else:
+ logger.warning("The data flow type could not be found. It is set to 'int'.")
+ state.output_data_ports[data_flow.from_key] = OutputDataPort('output_' + str(len(state.output_data_ports)),
+ data_type,
+ default_value,
+ data_flow.from_key,
+ state)
+ elif data_flow.from_state == same_level_state.state_id and data_flow.to_state == state.state_id:
+ if data_flow.from_key in same_level_state.input_data_ports:
+ data_type = same_level_state.input_data_ports[data_flow.from_key].data_type
+ default_value = same_level_state.input_data_ports[data_flow.from_key].default_value
+ elif data_flow.from_key in same_level_state.output_data_ports:
+ data_type = same_level_state.output_data_ports[data_flow.from_key].data_type
+ default_value = same_level_state.output_data_ports[data_flow.from_key].default_value
+ elif data_flow.from_key in same_level_state.scoped_variables:
+ data_type = same_level_state.scoped_variables[data_flow.from_key].data_type
+ default_value = same_level_state.scoped_variables[data_flow.from_key].default_value
+ else:
+ logger.warning("The data flow type could not be found. It is set to 'int'.")
+ state.input_data_ports[data_flow.to_key] = InputDataPort('input_' + str(len(state.input_data_ports)),
+ data_type,
+ default_value,
+ data_flow.to_key,
+ state)
+ elif hasattr(state, 'states'):
+ queue.extend(state.states.values())
+
+
def load_state_from_path(state_path):
"""Loads a state from a given path
@@ -378,6 +433,14 @@ def load_state_from_path(state_path):
return load_state_recursively(parent=None, state_path=state_path)
+def get_core_data_path(state_path):
+ return os.path.join(state_path, FILE_NAME_CORE_DATA)
+
+
+def get_meta_data_path(state_path):
+ return os.path.join(state_path, FILE_NAME_META_DATA)
+
+
def load_state_recursively(parent, state_path=None, dirty_states=[]):
"""Recursively loads the state
@@ -391,28 +454,42 @@ def load_state_recursively(parent, state_path=None, dirty_states=[]):
from rafcon.core.states.execution_state import ExecutionState
from rafcon.core.states.container_state import ContainerState
from rafcon.core.states.hierarchy_state import HierarchyState
+ from rafcon.core.singleton import library_manager
- path_core_data = os.path.join(state_path, FILE_NAME_CORE_DATA)
+ path_core_data = get_core_data_path(state_path)
+ path_meta_data = get_meta_data_path(state_path)
logger.debug("Load state recursively: {0}".format(str(state_path)))
- # TODO: Should be removed with next minor release
- if not os.path.exists(path_core_data):
- path_core_data = os.path.join(state_path, FILE_NAME_CORE_DATA_OLD)
-
try:
state_info = load_data_file(path_core_data)
except ValueError as e:
logger.exception("Error while loading state data: {0}".format(e))
return
except LibraryNotFoundException as e:
- if global_config.get_config_value("RAISE_ERROR_ON_MISSING_LIBRARY_STATES", False):
- raise e
+ if global_config.get_config_value("RAISE_ERROR_ON_MISSING_LIBRARY_STATES", False) or not library_manager.show_dialog:
+ raise
logger.error("Library could not be loaded: {0}\n"
"Skipping library and continuing loading the state machine".format(e))
state_info = storage_utils.load_objects_from_json(path_core_data, as_dict=True)
+ missing_library_meta_data = None
+ if os.path.exists(path_meta_data):
+ missing_library_meta_data = Vividict(storage_utils.load_objects_from_json(path_meta_data))
state_id = state_info["state_id"]
- dummy_state = HierarchyState(LIBRARY_NOT_FOUND_DUMMY_STATE_NAME, state_id=state_id)
+ outcomes = {outcome['outcome_id']: Outcome(outcome['outcome_id'], outcome['name']) for outcome in state_info["outcomes"].values()}
+ dummy_state = HierarchyState(LIBRARY_NOT_FOUND_DUMMY_STATE_NAME,
+ state_id=state_id,
+ outcomes=outcomes,
+ is_dummy=True,
+ missing_library_meta_data=missing_library_meta_data)
+ library_name = state_info['library_name']
+ path_parts = os.path.join(state_info['library_path'], library_name).split(os.sep)
+ dummy_state.description = 'The Missing Library Path: %s\nThe Missing Library Name: %s\n\n' % (state_info['library_path'], library_name)
+ from rafcon.core.singleton import library_manager
+ if path_parts[0] in library_manager.library_root_paths:
+ dummy_state.description += 'The Missing Library OS Path: %s' % os.path.join(library_manager.library_root_paths[path_parts[0]], *path_parts[1:])
+ else:
+ dummy_state.description += 'The missing library was located in the missing library root "%s"' % path_parts[0]
# set parent of dummy state
if isinstance(parent, ContainerState):
parent.add_state(dummy_state, storage_load=True)
@@ -462,24 +539,21 @@ def load_state_recursively(parent, state_path=None, dirty_states=[]):
child_state = load_state_recursively(state, child_state_path, dirty_states)
if not child_state:
return None
- if child_state.name is LIBRARY_NOT_FOUND_DUMMY_STATE_NAME:
- one_of_my_child_states_not_found = True
- if one_of_my_child_states_not_found:
- # omit adding transitions and data flows in this case
- pass
- else:
- # Now we can add transitions and data flows, as all child states were added
- if isinstance(state_info, tuple):
- # safe version
- # state.transitions = transitions
- # state.data_flows = data_flows
+ # Now we can add transitions and data flows, as all child states were added
+ if isinstance(state_info, tuple):
+ safe_init = global_config.get_config_value("LOAD_SM_WITH_CHECKS", True)
+ if safe_init:
+ # this will trigger all validity checks the state machine
+ state.transitions = transitions
+ else:
state._transitions = transitions
- for _, transition in state.transitions.items():
- transition._parent = ref(state)
state._data_flows = data_flows
- for _, data_flow in state.data_flows.items():
- data_flow._parent = ref(state)
+ for _, transition in state.transitions.items():
+ transition._parent = ref(state)
+ state._data_flows = data_flows
+ for _, data_flow in state.data_flows.items():
+ data_flow._parent = ref(state)
state.file_system_path = state_path
@@ -505,7 +579,7 @@ def limit_text_max_length(text, max_length, separator='_'):
"""
Limits the length of a string. The returned string will be the first `max_length/2` characters of the input string
plus a separator plus the last `max_length/2` characters of the input string.
-
+
:param text: the text to be limited
:param max_length: the maximum length of the output string
:param separator: the separator between the first "max_length"/2 characters of the input string and
@@ -572,3 +646,37 @@ def get_storage_id_for_state(state):
return limit_text_to_be_path_element(state.name, max_length) + ID_NAME_DELIMITER + state.state_id
else:
return state.state_id
+
+
+def find_library_dependencies_via_grep(library_root_path, library_path=None, library_name=None):
+ """ Find the dependencies of a library via grep
+
+ :param str library_root_path: the library root path
+ :param str library_path: the library path
+ :param str library_name: the library name
+
+ :rtype list(str)
+ :return: library dependency paths
+ """
+
+ library_dependency_paths = []
+ command_library_path = 'grep -r -l -E \'"library_path": "%s"|"library_path": "%s/(.*)"\' --include \\*.json %s' % (library_path, library_path, library_root_path)
+ command_library_name = 'grep -r -l \'"library_name": "%s"\' --include \\*.json %s' % (library_name, library_root_path)
+ if library_path is None and library_name is None:
+ return library_dependency_paths
+ elif library_path is None and library_name is not None:
+ final_findings = set(os.popen(command_library_name).read().splitlines())
+ elif library_path is not None and library_name is None:
+ final_findings = set(os.popen(command_library_path).read().splitlines())
+ else:
+ path_findings = set(os.popen(command_library_path).read().splitlines())
+ name_findings = set(os.popen(command_library_name).read().splitlines())
+ final_findings = path_findings.intersection(name_findings)
+ for library_dependency_path in final_findings:
+ parent = library_dependency_path
+ while True:
+ parent = os.path.dirname(parent)
+ if os.path.exists(os.path.join(parent, 'statemachine.json')):
+ break
+ library_dependency_paths.append(parent)
+ return set(library_dependency_paths)
\ No newline at end of file
diff --git a/source/rafcon/gui/config.py b/source/rafcon/gui/config.py
index 53e22d378..611ee4c71 100644
--- a/source/rafcon/gui/config.py
+++ b/source/rafcon/gui/config.py
@@ -90,8 +90,8 @@ def configure_gtk(self):
from gi.repository import Gtk
settings = Gtk.Settings.get_default()
if settings:
- settings.set_property("gtk-enable-animations", True)
settings.set_property("gtk-theme-name", theme_name)
+ settings.set_property("gtk-enable-animations", True)
settings.set_property("gtk-application-prefer-dark-theme", dark_theme)
Gtk.Window.set_default_icon_name("rafcon" if dark_theme else "rafcon-light")
diff --git a/source/rafcon/gui/controllers/execution_history.py b/source/rafcon/gui/controllers/execution_history.py
index 3be343215..44694b122 100644
--- a/source/rafcon/gui/controllers/execution_history.py
+++ b/source/rafcon/gui/controllers/execution_history.py
@@ -18,6 +18,7 @@
.. module:: execution_history
:synopsis: A module holding a controller for the ExecutionHistoryView holding information about the
execution history in a execution tree
+ :noindex:
"""
@@ -33,10 +34,10 @@
import rafcon
from rafcon.core.state_machine_manager import StateMachineManager
-from rafcon.core.execution.execution_history import ConcurrencyItem, CallItem, ScopedDataItem, HistoryItem
+from rafcon.core.execution.execution_history_items import HistoryItem, StateMachineStartItem, ScopedDataItem, CallItem, \
+ ConcurrencyItem, CallType
from rafcon.core.singleton import state_machine_execution_engine
from rafcon.core.execution.execution_status import StateMachineExecutionStatus
-from rafcon.core.execution.execution_history import CallType, StateMachineStartItem
from rafcon.gui.controllers.utils.extended_controller import ExtendedController
from rafcon.gui.models.state_machine_manager import StateMachineManagerModel
@@ -124,7 +125,8 @@ def open_selected_history_separately(self, widget, event=None):
"The external execution history viewer can only open finished executions.")
return
- if execution_history.execution_history_storage and execution_history.execution_history_storage.filename:
+ if execution_history.consumer_manager.file_system_consumer_exists and \
+ execution_history.consumer_manager.get_file_system_consumer_file_name():
from rafcon.gui.utils.shell_execution import execute_command_in_process
gui_path = path.dirname(path.dirname(path.realpath(__file__)))
source_path = path.dirname(path.dirname(gui_path))
@@ -135,12 +137,13 @@ def open_selected_history_separately(self, widget, event=None):
python_version = "python2"
else:
python_version = "python3"
- cmd = "{python_version} {path} {filename} {run_id}" \
+ cmd = "{python_version} {path} '{filename}' '{run_id}'" \
"".format(python_version=python_version, path=viewer_path,
- filename=execution_history.execution_history_storage.filename, run_id=run_id)
+ filename=execution_history.consumer_manager.get_file_system_consumer_file_name(),
+ run_id=run_id)
execute_command_in_process(cmd, shell=True, cwd=source_path, logger=logger)
else:
- logger.info("Set EXECUTION_LOG_ENABLE to True in your config in order to "
+ logger.info("Set FILE_SYSTEM_EXECUTION_HISTORY_ENABLE to True in your config in order to "
"activate execution file logging and to use the external execution history viewer.")
def append_string_to_menu(self, popup_menu, menu_item_string):
diff --git a/source/rafcon/gui/controllers/global_variable_manager.py b/source/rafcon/gui/controllers/global_variable_manager.py
index 86a184dec..ca4c1b40a 100644
--- a/source/rafcon/gui/controllers/global_variable_manager.py
+++ b/source/rafcon/gui/controllers/global_variable_manager.py
@@ -18,6 +18,7 @@
.. module:: global_variable_manager
:synopsis: A module that holds the controller to access the GlobalVariableManager by a GUI based on the
GlobalVariableEditorView.
+ :noindex:
"""
diff --git a/source/rafcon/gui/controllers/library_tree.py b/source/rafcon/gui/controllers/library_tree.py
index 71985e89a..e5ba7ebc0 100644
--- a/source/rafcon/gui/controllers/library_tree.py
+++ b/source/rafcon/gui/controllers/library_tree.py
@@ -29,6 +29,7 @@
from functools import partial
from rafcon.core.states.library_state import LibraryState
+from rafcon.core.storage import storage
from rafcon.gui.config import global_gui_config
from rafcon.gui.runtime_config import global_runtime_config
from rafcon.gui.controllers.utils.extended_controller import ExtendedController
@@ -37,7 +38,8 @@
from rafcon.gui.helpers.text_formatting import format_folder_name_human_readable
import rafcon.gui.singleton as gui_singletons
from rafcon.gui.utils import constants
-from rafcon.gui.utils.dialog import RAFCONButtonDialog
+from rafcon.gui.utils.dialog import RAFCONButtonDialog, RAFCONInputDialog
+from rafcon.gui.interface import open_folder
from rafcon.utils import log
logger = log.get_logger(__name__)
@@ -52,12 +54,22 @@ class LibraryTreeController(ExtendedController):
TOOL_TIP_STORAGE_ID = 3
LIB_KEY_STORAGE_ID = 4
- def __init__(self, model, view):
+ def __init__(self, model, view, find_usages=False):
assert isinstance(model, LibraryManagerModel)
assert isinstance(view, Gtk.TreeView)
ExtendedController.__init__(self, model, view)
self.tree_store = Gtk.TreeStore(GObject.TYPE_STRING, GObject.TYPE_PYOBJECT, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
- view.set_model(self.tree_store)
+ self.filter = self.tree_store.filter_new()
+ self.filter_value = ''
+ self.find_usages = find_usages
+
+ if find_usages:
+ self.filter.set_visible_func(self._library_usages_filter)
+ self.usages = None
+ else:
+ self.filter.set_visible_func(self._search_filter)
+
+ view.set_model(self.filter)
view.set_tooltip_column(3)
# Gtk TODO: solve via Gtk.TargetList? https://python-gtk-3-tutorial.readthedocs.io/en/latest/drag_and_drop.html
@@ -86,8 +98,12 @@ def generate_right_click_menu(self, kind='library'):
menu.append(create_menu_item("Open", constants.BUTTON_OPEN, self.open_button_clicked))
menu.append(create_menu_item("Open and run", constants.BUTTON_START, self.open_run_button_clicked))
menu.append(Gtk.SeparatorMenuItem())
+ menu.append(create_menu_item("Rename library", constants.BUTTON_RENAME,
+ self.menu_item_rename_libraries_or_root_clicked))
menu.append(create_menu_item("Remove library", constants.BUTTON_DEL,
self.menu_item_remove_libraries_or_root_clicked))
+ menu.append(create_menu_item("Relocate library", constants.BUTTON_RELOCATE,
+ self.menu_item_relocate_libraries_or_root_clicked))
sub_menu_item, sub_menu = append_sub_menu_to_parent_menu("Substitute as library", menu,
constants.BUTTON_REFR)
@@ -104,15 +120,25 @@ def generate_right_click_menu(self, kind='library'):
partial(self.substitute_as_template_clicked, keep_name=True)))
sub_menu.append(create_menu_item("Take name from Library", constants.BUTTON_EXCHANGE,
partial(self.substitute_as_template_clicked, keep_name=False)))
+
+ menu.append(create_menu_item("Find usages in libraries", constants.ICON_FIND_USAGES,
+ self.menu_item_find_usages_clicked))
+
elif kind in ['library root', 'library tree']:
- menu.append(create_menu_item("Add library root", constants.BUTTON_DEL,
+ menu.append(create_menu_item("Add library root", constants.BUTTON_NEW,
self.menu_item_add_library_root_clicked))
if kind == 'library root':
+ menu.append(create_menu_item("Rename library root", constants.BUTTON_RENAME,
+ self.menu_item_rename_libraries_or_root_clicked))
menu.append(create_menu_item("Remove library root", constants.BUTTON_DEL,
self.menu_item_remove_libraries_or_root_clicked))
+ menu.append(create_menu_item("Relocate library root", constants.BUTTON_RELOCATE,
+ self.menu_item_relocate_libraries_or_root_clicked))
elif kind == 'libraries':
menu.append(create_menu_item("Remove libraries", constants.BUTTON_DEL,
self.menu_item_remove_libraries_or_root_clicked))
+ menu.append(create_menu_item("Relocate libraries", constants.BUTTON_RELOCATE,
+ self.menu_item_relocate_libraries_or_root_clicked))
return menu
@@ -129,6 +155,19 @@ def mouse_click(self, widget, event=None):
self.view.expand_to_path(state_row_path)
return False
self.open_button_clicked(None)
+
+ if self.find_usages:
+ state_machine_model = gui_singletons.state_machine_manager_model.get_selected_state_machine_model()
+ selected_states = []
+ queue = [state_machine_model.root_state]
+ while len(queue) > 0:
+ state = queue.pop(0)
+ if hasattr(state.state, 'lib_os_path') and state.state.library_path == self.filter_value[0] and state.state.library_name == self.filter_value[1]:
+ selected_states.append(state)
+ elif hasattr(state, 'states'):
+ queue.extend(state.states.values())
+ state_machine_model.selection.set(selected_states)
+
return True
# Single right click
@@ -186,7 +225,8 @@ def redo_expansion_state(self):
self.view.expand_to_path(library_row_path)
# print(library_path)
except (TypeError, KeyError):
- logger.warning("expansion state of library tree could not be re-done")
+ pass
+ # logger.warning("expansion state of library tree could not be re-done")
def update(self):
self.store_expansion_state()
@@ -281,7 +321,10 @@ def select_library_tree_element_of_lib_tree_path(self, lib_tree_path):
if state_row_path is not None:
self.view.expand_to_path(state_row_path)
self.view.scroll_to_cell(state_row_path, None)
- self.view.get_selection().select_iter(library_state_row_iter)
+ # Important: do not use select_iter() here!
+ # The iters of the view (whose model is the TreeModelFilter) won't match
+ # the iters stored in library_row_iter_dict_by_library_path as those directly point to the TreeModel
+ self.view.get_selection().select_path(state_row_path)
self.view.grab_focus()
def select_library_tree_element_of_library_state_model(self, state_m):
@@ -349,6 +392,51 @@ def menu_item_add_library_root_clicked(self, widget):
global_config.save_configuration()
self.model.library_manager.refresh_libraries()
+ def menu_item_rename_libraries_or_root_clicked(self, menu_item):
+ """Rename library after request second confirmation"""
+
+ import rafcon.gui.helpers.state_machine as gui_helper_state_machine
+ menu_item_text = self.get_menu_item_text(menu_item)
+ logger.info("Rename item '{0}' pressed.".format(menu_item_text))
+ model, path = self.view.get_selection().get_selected()
+ if path:
+ tree_m_row = self.filter[path]
+ library_os_path, library_path, library_name, item_key = self.extract_library_properties_from_selected_row()
+ library_file_system_path = library_os_path
+ if "root" in menu_item_text:
+ button_texts = [menu_item_text + "from tree and config", "Cancel"]
+ partial_message = "This will remove the library root from your configuration (config.yaml)."
+ else:
+ button_texts = [menu_item_text, "Cancel"]
+ partial_message = "This folder will be renamed on the hard drive! Do you really want to do that?"
+ message_string = "You have chosen to {2} with " \
+ "\n\nlibrary tree path: {0}" \
+ "\n\nphysical path: {1}\n\n\n" \
+ "{3}" \
+ "".format(os.path.join(self.convert_if_human_readable(tree_m_row[self.LIB_PATH_STORAGE_ID]), item_key),
+ library_file_system_path,
+ menu_item_text.lower(),
+ partial_message)
+ width = 8 * len("physical path: " + library_file_system_path)
+ dialog = RAFCONInputDialog(message_string,
+ button_texts,
+ message_type=Gtk.MessageType.QUESTION,
+ parent=self.get_root_window(),
+ width=min(width, 1400))
+ dialog.set_entry_text(item_key)
+ response_id = dialog.run()
+ new_name = dialog.get_entry_text()
+ dialog.destroy()
+ parent_library_os_path = os.path.abspath(os.path.join(library_os_path, os.pardir))
+ new_library_os_path = os.path.join(parent_library_os_path, new_name)
+ if response_id == 1:
+ if "root" in menu_item_text:
+ gui_helper_state_machine.rename_library_root(tree_m_row[self.LIB_KEY_STORAGE_ID], new_name, logger)
+ else:
+ gui_helper_state_machine.rename_library(library_os_path, new_library_os_path, library_path, library_name, new_name, logger)
+ return True
+ return False
+
def menu_item_remove_libraries_or_root_clicked(self, menu_item):
"""Removes library from hard drive after request second confirmation"""
@@ -358,7 +446,7 @@ def menu_item_remove_libraries_or_root_clicked(self, menu_item):
model, path = self.view.get_selection().get_selected()
if path:
# Second confirmation to delete library
- tree_m_row = self.tree_store[path]
+ tree_m_row = self.filter[path]
library_os_path, library_path, library_name, item_key = self.extract_library_properties_from_selected_row()
# assert isinstance(tree_m_row[self.ITEM_STORAGE_ID], str)
library_file_system_path = library_os_path
@@ -368,11 +456,11 @@ def menu_item_remove_libraries_or_root_clicked(self, menu_item):
partial_message = "This will remove the library root from your configuration (config.yaml)."
else:
button_texts = [menu_item_text, "Cancel"]
- partial_message = "This folder will be removed from hard drive! You really wanna do that?"
+ partial_message = "This folder will be removed from your hard drive! Do you really want to do that?"
- message_string = "You choose to {2} with " \
+ message_string = "You have chosen to {2} with " \
"\n\nlibrary tree path: {0}" \
- "\n\nphysical path: {1}.\n\n\n"\
+ "\n\nphysical path: {1}\n\n\n" \
"{3}" \
"".format(os.path.join(self.convert_if_human_readable(tree_m_row[self.LIB_PATH_STORAGE_ID]),
item_key),
@@ -411,6 +499,38 @@ def menu_item_remove_libraries_or_root_clicked(self, menu_item):
return True
return False
+ def menu_item_relocate_libraries_or_root_clicked(self, menu_item):
+ """Relocate library after request second confirmation"""
+
+ import rafcon.gui.helpers.state_machine as gui_helper_state_machine
+ menu_item_text = self.get_menu_item_text(menu_item)
+ model, path = self.view.get_selection().get_selected()
+ if path:
+ tree_m_row = self.filter[path]
+ library_os_path, library_path, library_name, item_key = self.extract_library_properties_from_selected_row()
+ new_directory = open_folder('Select the new directory')
+ if new_directory:
+ if 'root' in menu_item_text:
+ gui_helper_state_machine.relocate_library_root(tree_m_row[self.LIB_KEY_STORAGE_ID], new_directory, logger)
+ elif 'libraries' in menu_item_text:
+ gui_helper_state_machine.relocate_libraries(library_os_path.replace('[source]:\n', ''), item_key, new_directory, logger)
+ else:
+ gui_helper_state_machine.relocate_library(library_os_path, library_path, library_name, new_directory, logger)
+ return True
+ return False
+
+ def menu_item_find_usages_clicked(self, widget):
+ library_os_path, library_path, library_name, item_key = self.extract_library_properties_from_selected_row()
+ library_usages_controller = gui_singletons.main_window_controller.get_controller('library_usages_controller')
+ library_usages_controller.filter_value = [library_path, library_name]
+ usages = []
+ for root in library_usages_controller.model.library_manager.library_root_paths.values():
+ usages.extend(storage.find_library_dependencies_via_grep(root, library_usages_controller.filter_value[0], library_usages_controller.filter_value[1]))
+ library_usages_controller.usages = usages
+ library_usages_controller.filter.refilter()
+ library_usages_controller.view.expand_all()
+ gui_singletons.main_window_controller.upper_notebook.set_current_page(3)
+
def substitute_as_library_clicked(self, widget, keep_name=True):
import rafcon.gui.helpers.state_machine as gui_helper_state_machine
gui_helper_state_machine.substitute_selected_state(self._get_selected_library_state(), as_template=False,
@@ -421,6 +541,7 @@ def substitute_as_template_clicked(self, widget, keep_name=True):
gui_helper_state_machine.substitute_selected_state(self._get_selected_library_state(), as_template=True,
keep_name=keep_name)
+
def extract_library_properties_from_selected_row(self):
""" Extracts properties library_os_path, library_path, library_name and tree_item_key from tree store row """
(model, row) = self.view.get_selection().get_selected()
@@ -452,3 +573,39 @@ def _get_selected_library_state(self):
self.convert_if_human_readable(str(library_path)) + "/" + str(item_key)))
library_name = library_os_path.split(os.path.sep)[-1]
return LibraryState(library_path, library_name, "0.1", format_folder_name_human_readable(library_name))
+
+ def _search_filter(self, model, iter, data):
+ if not self.filter_value or self.filter_value in model.get_value(iter, self.ID_STORAGE_ID).lower():
+ return True
+ else:
+ parent_iter = model.iter_parent(iter)
+ while parent_iter is not None:
+ if self.filter_value in model.get_value(parent_iter, self.ID_STORAGE_ID).lower():
+ return True
+ parent_iter = model.iter_parent(parent_iter)
+ queue = [iter]
+ while len(queue) > 0:
+ node_iter = queue.pop(0)
+ if model.iter_has_child(node_iter):
+ for i in range(model.iter_n_children(node_iter)):
+ queue.append(model.iter_nth_child(node_iter, i))
+ if self.filter_value in model.get_value(node_iter, self.ID_STORAGE_ID).lower():
+ self.view.expand_all()
+ return True
+ return False
+
+ def _library_usages_filter(self, model, iter, data):
+ if self.filter_value:
+ if not model.iter_has_child(iter):
+ return model.get_value(iter, self.ITEM_STORAGE_ID) in self.usages
+ else:
+ queue = [iter]
+ while len(queue) > 0:
+ node_iter = queue.pop(0)
+ if model.iter_has_child(node_iter):
+ for i in range(model.iter_n_children(node_iter)):
+ queue.append(model.iter_nth_child(node_iter, i))
+ else:
+ if model.get_value(node_iter, self.ITEM_STORAGE_ID) in self.usages:
+ return True
+ return False
\ No newline at end of file
diff --git a/source/rafcon/gui/controllers/main_window.py b/source/rafcon/gui/controllers/main_window.py
index 52a248455..9110f61fc 100644
--- a/source/rafcon/gui/controllers/main_window.py
+++ b/source/rafcon/gui/controllers/main_window.py
@@ -87,6 +87,8 @@ def __init__(self, state_machine_manager_model, view):
# shortcut manager
self.shortcut_manager = ShortcutManager(view['main_window'])
+ self.upper_notebook = view['upper_notebook']
+
######################################################
# debug console
######################################################
@@ -101,6 +103,13 @@ def __init__(self, state_machine_manager_model, view):
library_controller = LibraryTreeController(self.library_manager_model, view.library_tree)
self.add_controller('library_controller', library_controller)
+ ######################################################
+ # library usages tree
+ ######################################################
+ library_usages_controller = LibraryTreeController(self.library_manager_model, view.library_usages_tree, True)
+ self.add_controller('library_usages_controller', library_usages_controller)
+
+
######################################################
# state icons
######################################################
@@ -222,6 +231,14 @@ def destroy(self):
def update_widget_runtime_config(widget, event, name):
global_runtime_config.store_widget_properties(widget, name)
+ def update_search_bar_visibility(self, widget, event):
+ state_machine_search = self.view['state_machine_search']
+ state_machine_search.set_visible(widget.get_active())
+ if widget.get_active():
+ state_machine_search.grab_focus()
+ else:
+ state_machine_search.set_text('')
+
def register_view(self, view):
super(MainWindowController, self).register_view(view)
self.register_actions(self.shortcut_manager)
@@ -257,6 +274,8 @@ def register_view(self, view):
self.connect_button_to_function('button_start_shortcut', "toggled", self.on_button_start_shortcut_toggled)
self.connect_button_to_function('button_stop_shortcut', "clicked", self.on_button_stop_shortcut_clicked)
self.connect_button_to_function('button_pause_shortcut', "toggled", self.on_button_pause_shortcut_toggled)
+ self.connect_button_to_function('button_run_this_state_shortcut', "clicked",
+ self.on_button_start_this_state_shortcut_clicked)
self.connect_button_to_function('button_start_from_shortcut', "clicked",
self.on_button_start_from_shortcut_clicked)
self.connect_button_to_function('button_run_to_shortcut', "clicked",
@@ -277,6 +296,9 @@ def register_view(self, view):
"clicked",
self.on_button_step_backward_shortcut_clicked)
+ view['state_machine_search'].connect("search_changed", self.state_machine_search_changed)
+
+
view['upper_notebook'].connect('switch-page', self.on_notebook_tab_switch, view['upper_notebook_title'],
view.left_bar_window, 'upper')
view['lower_notebook'].connect('switch-page', self.on_notebook_tab_switch, view['lower_notebook_title'],
@@ -291,6 +313,7 @@ def register_view(self, view):
view['top_level_h_pane'].connect("button-release-event", self.update_widget_runtime_config, "LEFT_BAR_DOCKED")
view['right_h_pane'].connect("button-release-event", self.update_widget_runtime_config, "RIGHT_BAR_DOCKED")
view['central_v_pane'].connect("button-release-event", self.update_widget_runtime_config, "CONSOLE_DOCKED")
+ view['show_search_bar'].connect("toggled", self.update_search_bar_visibility, "TOGGLED")
# hide not usable buttons
self.view['step_buttons'].hide()
@@ -578,6 +601,9 @@ def on_button_pause_shortcut_toggled(self, widget, event=None):
if self.view['button_pause_shortcut'].get_active():
self.get_controller('menu_bar_controller').on_pause_activate(None)
+ def on_button_start_this_state_shortcut_clicked(self, widget, event=None):
+ self.get_controller('menu_bar_controller').on_run_selected_state_activate(None)
+
def on_button_start_from_shortcut_clicked(self, widget, event=None):
self.get_controller('menu_bar_controller').on_start_from_selected_state_activate(None)
@@ -600,6 +626,12 @@ def on_button_step_out_shortcut_clicked(self, widget, event=None):
def on_button_step_backward_shortcut_clicked(self, widget, event=None):
self.get_controller('menu_bar_controller').on_backward_step_activate(None)
+ def state_machine_search_changed(self, search):
+ library_controller = self.get_controller('library_controller')
+ library_controller.view.collapse_all()
+ library_controller.filter_value = search.get_text().lower()
+ library_controller.filter.refilter()
+
def on_notebook_tab_switch(self, notebook, page, page_num, title_label, window, notebook_identifier):
"""Triggered whenever a left-bar notebook tab is changed.
@@ -625,6 +657,10 @@ def on_switch_page_check_collapse_button(self, notebook, page_num):
self.view["collapse_tree_button"].show()
else:
self.view["collapse_tree_button"].hide()
+ if upper_notebook_title == 'LIBRARIES':
+ self.view["show_search_bar"].show()
+ else:
+ self.view["show_search_bar"].hide()
def on_collapse_button_clicked(self, button):
upper_page_num = self.view['upper_notebook'].get_current_page()
diff --git a/source/rafcon/gui/controllers/menu_bar.py b/source/rafcon/gui/controllers/menu_bar.py
index 188301b2c..fd04753e9 100644
--- a/source/rafcon/gui/controllers/menu_bar.py
+++ b/source/rafcon/gui/controllers/menu_bar.py
@@ -147,6 +147,7 @@ def register_view(self, view):
self.connect_button_to_function('save_state_as', 'activate', self.on_save_selected_state_as_activate)
self.connect_button_to_function('undo', 'activate', self.on_undo_activate)
self.connect_button_to_function('redo', 'activate', self.on_redo_activate)
+ self.connect_button_to_function('search', 'activate', self.on_search_activate)
self.connect_button_to_function('grid', 'activate', self.on_grid_toggled)
self.connect_button_to_function('data_flow_mode', 'toggled', self.on_data_flow_mode_toggled)
@@ -159,6 +160,7 @@ def register_view(self, view):
self.connect_button_to_function('start', 'activate', self.on_start_activate)
self.connect_button_to_function('start_from_selected', 'activate', self.on_start_from_selected_state_activate)
self.connect_button_to_function('run_to_selected', 'activate', self.on_run_to_selected_state_activate)
+ self.connect_button_to_function('run_selected', 'activate', self.on_run_selected_state_activate)
self.connect_button_to_function('pause', 'activate', self.on_pause_activate)
self.connect_button_to_function('stop', 'activate', self.on_stop_activate)
self.connect_button_to_function('step_mode', 'activate', self.on_step_mode_activate)
@@ -352,6 +354,8 @@ def register_actions(self, shortcut_manager):
"on_start_from_selected_state_activate"))
self.add_callback_to_shortcut_manager('run_to_selected', partial(self.call_action_callback,
"on_run_to_selected_state_activate"))
+ self.add_callback_to_shortcut_manager('run_selected', partial(self.call_action_callback,
+ "on_run_selected_state_activate"))
self.add_callback_to_shortcut_manager('stop', partial(self.call_action_callback, "on_stop_activate"))
self.add_callback_to_shortcut_manager('pause', partial(self.call_action_callback, "on_pause_activate"))
@@ -369,6 +373,8 @@ def register_actions(self, shortcut_manager):
self.add_callback_to_shortcut_manager('fullscreen', self.on_toggle_full_screen_mode)
+ self.add_callback_to_shortcut_manager('search', self.on_search_activate)
+
def call_action_callback(self, callback_name, *args, **kwargs):
"""Wrapper for action callbacks
@@ -588,6 +594,11 @@ def on_undo_activate(self, widget, data=None):
def on_redo_activate(self, widget, data=None):
self.shortcut_manager.trigger_action("redo", None, None)
+ def on_search_activate(self, widget, data=None, cursor_position=None):
+ show_search_bar = self.main_window_view["show_search_bar"]
+ show_search_bar.set_active(not show_search_bar.get_active())
+ return True
+
def on_grid_toggled(self, widget, data=None):
pass
@@ -665,6 +676,15 @@ def on_start_from_selected_state_activate(self, widget, data=None):
self.state_machine_execution_engine.start(self.model.selected_state_machine_id,
selection.get_selected_state().state.get_path())
+ def on_run_selected_state_activate(self, widget, data=None):
+ logger.debug("Run selected state ...")
+ selection = gui_singletons.state_machine_manager_model.get_selected_state_machine_model().selection
+ if len(selection.states) is not 1:
+ logger.error("Exactly one state must be selected!")
+ else:
+ self.state_machine_execution_engine.run_selected_state(selection.get_selected_state().state.get_path(),
+ self.model.selected_state_machine_id)
+
def on_pause_activate(self, widget, data=None):
self.state_machine_execution_engine.pause()
diff --git a/source/rafcon/gui/controllers/modification_history.py b/source/rafcon/gui/controllers/modification_history.py
index 5733956dc..87bfbea64 100755
--- a/source/rafcon/gui/controllers/modification_history.py
+++ b/source/rafcon/gui/controllers/modification_history.py
@@ -16,6 +16,7 @@
"""
.. module:: modification_history
:synopsis: A module that holds the controller to list and access the modification-history.
+ :noindex:
"""
diff --git a/source/rafcon/gui/controllers/notification_bar.py b/source/rafcon/gui/controllers/notification_bar.py
index dd7d30e1f..8b30477f6 100644
--- a/source/rafcon/gui/controllers/notification_bar.py
+++ b/source/rafcon/gui/controllers/notification_bar.py
@@ -8,6 +8,8 @@
# Contributors:
# Franz Steinmetz
+import time
+
from gi.repository import Gtk
from rafcon.gui.controllers.utils.extended_controller import ExtendedController
@@ -21,6 +23,7 @@ class NotificationBarController(ExtendedController):
def __init__(self, model, view):
super(NotificationBarController, self).__init__(model, view)
log_helpers.LoggingViewHandler.add_logging_view(self.__class__.__name__, self)
+ self.last_notification_timestamp = None
def register_view(self, view):
super(NotificationBarController, self).register_view(view)
@@ -39,7 +42,10 @@ def print_message(self, message, log_level):
if not self._handle_log_level(log_level):
return
message_text = ": ".join(message.split(": ")[2:]) # remove time, log level and source
- self.view.show_notification(message_text, log_level)
+ now = time.time() * 1000
+ if self.last_notification_timestamp is None or self.last_notification_timestamp + 100 < now:
+ self.view.show_notification(message_text, log_level)
+ self.last_notification_timestamp = now
def _handle_log_level(self, log_level):
minimum_low_level = self.model.config.get_config_value("NOTIFICATIONS_MINIMUM_LOG_LEVEL", 30)
@@ -49,4 +55,4 @@ def _handle_response(self, widget, response_id):
if response_id == Gtk.ResponseType.CLOSE:
if self.view.timer:
self.view.timer.cancel()
- self.view.hide_bar()
+ self.view.hide_bar()
\ No newline at end of file
diff --git a/source/rafcon/gui/controllers/right_click_menu/state.py b/source/rafcon/gui/controllers/right_click_menu/state.py
index 34bb9d871..586813145 100644
--- a/source/rafcon/gui/controllers/right_click_menu/state.py
+++ b/source/rafcon/gui/controllers/right_click_menu/state.py
@@ -225,6 +225,10 @@ def insert_execution_sub_menu_in_menu(self, menu, shortcuts_dict, accel_group):
self.on_run_to_selected_state_activate,
accel_code=shortcuts_dict['run_to_selected'][0],
accel_group=accel_group))
+ execution_sub_menu.append(create_menu_item("run this state", constants.BUTTON_RUN_SELECTED_STATE,
+ self.on_run_selected_state_activate,
+ accel_code=shortcuts_dict['run_selected'][0],
+ accel_group=accel_group))
def insert_copy_cut_paste_in_menu(self, menu, shortcuts_dict, accel_group, no_paste=False):
menu.append(create_menu_item("Copy selection", constants.BUTTON_COPY, self.on_copy_activate,
@@ -280,6 +284,8 @@ def on_select_library_tree_element(widget, date=None, state_m=None):
from rafcon.gui.singleton import main_window_controller
library_tree_controller = main_window_controller.get_controller('library_controller')
library_tree_controller.select_library_tree_element_of_library_state_model(state_m)
+ library_usages_tree_controller = main_window_controller.get_controller('library_usages_controller')
+ library__usagestree_controller.select_library_tree_element_of_library_state_model(state_m)
def on_toggle_is_start_state(self, widget, data=None):
self.shortcut_manager.trigger_action("is_start_state", None, None)
@@ -335,6 +341,9 @@ def on_run_from_selected_state_activate(self, widget, data=None):
def on_run_to_selected_state_activate(self, widget, data=None):
self.shortcut_manager.trigger_action('run_to_selected', None, None)
+ def on_run_selected_state_activate(self, widget, data=None):
+ self.shortcut_manager.trigger_action('run_selected', None, None)
+
@staticmethod
def on_save_as_activate(widget, data=None, path=None, save_as_function=None):
# workaround to set the initial path in the 'choose folder' dialog to the handed one
@@ -420,36 +429,6 @@ def activate_menu(self, event, menu):
return True
-class StateRightClickMenuControllerOpenGLEditor(StateMachineRightClickMenuController):
-
- def register_view(self, view):
- ExtendedController.register_view(self, view)
- from rafcon.gui.views.graphical_editor import GraphicalEditorView
- assert isinstance(view, GraphicalEditorView)
- view.editor.connect('button_press_event', self.mouse_click)
-
- def activate_menu(self, event, menu):
- # logger.info("activate_menu by " + self.__class__.__name__)
- selection = gui_singletons.state_machine_manager_model.get_selected_state_machine_model().selection
- if len(selection.states) > 0 or len(selection.scoped_variables) > 0:
- menu.popup(None, None, None, None, event.get_button()[1], event.time)
- return True
- else:
- return False
-
- def on_copy_activate(self, widget, data=None):
- # logger.info("trigger opengl copy")
- self.shortcut_manager.trigger_action("copy", None, None)
-
- def on_paste_activate(self, widget, data=None):
- # logger.info("trigger opengl paste")
- self.shortcut_manager.trigger_action("paste", None, None)
-
- def on_cut_activate(self, widget, data=None):
- # logger.info("trigger opengl cut")
- self.shortcut_manager.trigger_action("cut", None, None)
-
-
class StateRightClickMenuGaphas(StateMachineRightClickMenu):
""" A class for handling the right click menu inside gaphas
diff --git a/source/rafcon/gui/controllers/state_editor/data_flows.py b/source/rafcon/gui/controllers/state_editor/data_flows.py
index 690e2bd98..2a41fcff8 100644
--- a/source/rafcon/gui/controllers/state_editor/data_flows.py
+++ b/source/rafcon/gui/controllers/state_editor/data_flows.py
@@ -549,7 +549,7 @@ def take_from_dict(from_dict, key):
from_key_port.name
to_key_port = to_state.get_data_port_by_id(data_flow.to_key)
- # to_key_label = ''
+ to_key_label = ''
if to_key_port is not None:
to_key_label = PORT_TYPE_TAG.get(type(to_key_port), 'None') + '.' + \
(to_key_port.data_type.__name__ or 'None') + '.' + \
diff --git a/source/rafcon/gui/controllers/state_machines_editor.py b/source/rafcon/gui/controllers/state_machines_editor.py
index 6a6280933..286c52477 100644
--- a/source/rafcon/gui/controllers/state_machines_editor.py
+++ b/source/rafcon/gui/controllers/state_machines_editor.py
@@ -335,6 +335,9 @@ def on_mouse_right_click(self, event, state_machine_m, result):
callback=self.on_close_clicked,
callback_args=[state_machine_m, None])
menu.append(menu_item)
+ menu_item = create_menu_item("Close All State Machines", constants.BUTTON_CLOSE,
+ callback=self.on_close_all_clicked)
+ menu.append(menu_item)
menu.show_all()
menu.popup(None, None, None, None, event.get_button()[1], event.time)
@@ -413,6 +416,9 @@ def push_sm_dirty_dialog():
remove_state_machine_m()
return True
+ def on_close_all_clicked(self, event):
+ self.close_all_pages(force=False)
+
def remove_state_machine_tab(self, state_machine_m):
"""
@@ -447,11 +453,11 @@ def remove_state_machine_tab(self, state_machine_m):
else:
self.model.selected_state_machine_id = None
- def close_all_pages(self):
+ def close_all_pages(self, force=True):
"""Closes all tabs of the state machines editor."""
state_machine_m_list = [tab['state_machine_m'] for tab in self.tabs.values()]
for state_machine_m in state_machine_m_list:
- self.on_close_clicked(None, state_machine_m, None, force=True)
+ self.on_close_clicked(None, state_machine_m, None, force)
def refresh_state_machines(self, state_machine_ids):
""" Refresh list af state machine tabs
diff --git a/source/rafcon/gui/glade/main_window.glade b/source/rafcon/gui/glade/main_window.glade
index 0281b4431..3e8ae14fd 100644
--- a/source/rafcon/gui/glade/main_window.glade
+++ b/source/rafcon/gui/glade/main_window.glade
@@ -1,5 +1,5 @@
-
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ True
+
+
+
+
+
+ True
+ True
+ 2
+
@@ -208,11 +264,35 @@
False
+
+
+ True
+ True
+
+
+
+
+
+ 3
+
+
+
+
+ True
+ False
+ Library Usages
+ 90
+
+
+ 3
+ False
+
+
True
True
- 1
+ 2
@@ -496,6 +576,18 @@
True
False
False
+
+
+ True
+ False
+ run_this_state
+ True
+
+
+ False
+ True
+
+
True
@@ -830,17 +922,5 @@
-
-
-
diff --git a/source/rafcon/gui/glade/menu_bar.glade b/source/rafcon/gui/glade/menu_bar.glade
index db09a2384..919fd5d24 100644
--- a/source/rafcon/gui/glade/menu_bar.glade
+++ b/source/rafcon/gui/glade/menu_bar.glade
@@ -1,5 +1,5 @@
-
+
@@ -251,6 +251,14 @@
True
+
+
+
@@ -358,6 +366,14 @@
True
+
+
+