diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ce72e40 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "modules/cxxopts"] + path = modules/cxxopts + url = https://github.com/jarro2783/cxxopts.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 03bbc68..f8d7bdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,188 +1,197 @@ cmake_minimum_required(VERSION 3.3.0) # Start project, set version here. project("RoSA") # NOTE: Adjust the variables version and release in docs/conf.py according to # version changes here. set(ROSA_VERSION_MAJOR 0) set(ROSA_VERSION_MINOR 1) set(ROSA_VERSION_PATCH 0) # Add path for custom modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ) # Package information if( NOT PACKAGE_VERSION ) set(PACKAGE_VERSION "${ROSA_VERSION_MAJOR}.${ROSA_VERSION_MINOR}.${ROSA_VERSION_PATCH}") endif() set(PACKAGE_NAME "Research on Self-Awareness Framework") set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(PACKAGE_BUGREPORT "david.juhasz@tuwien.ac.at") #TODO: cpack? # Sanity check we are to make out-of-tree build if( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR ) message(FATAL_ERROR "In-source builds are not allowed.") endif() string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) # Set various paths set(ROSA_MAIN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(ROSA_MAIN_INCLUDE_DIR ${ROSA_MAIN_SRC_DIR}/include) set(ROSA_MAIN_BIN_DIR ${ROSA_MAIN_SRC_DIR}/bin) set(ROSA_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(ROSA_RUNTIME_OUTPUT_INTDIR ${ROSA_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) set(ROSA_LIBRARY_OUTPUT_INTDIR ${ROSA_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib) set(ROSA_EXEC_BINARY_DIR ${ROSA_BINARY_DIR}/bin) set(ROSA_LIBRARY_DIR ${ROSA_BINARY_DIR}/lib) set(ROSA_INCLUDE_DIR ${ROSA_BINARY_DIR}/include) +set(ROSA_MAIN_MODULE_DIR ${ROSA_MAIN_SRC_DIR}/modules) # Add some generic helpers. include(AddCMakeTools) # Set build options option(ROSA_INCLUDE_TOOLS "Generate build targets for RoSA tools." ON) option(ROSA_INCLUDE_EXAMPLES "Generate build targets for RoSA examples." ON) option(ROSA_ENABLE_PEDANTIC "Compile with pedantic enabled." ON) # Assertions are always enabled for Debug builds, this option is respected only # for non-Debug builds. option(ROSA_ENABLE_ASSERTIONS "Enable assertions for non-Debug builds." OFF) option(ROSA_ENABLE_CLANG_TIDY "Run clang-tidy checks when building RoSA." OFF) option(ROSA_INCLUDE_CLANG_FORMAT "Generate build target for formatting RoSA sources with clang-format." OFF) option(ROSA_INCLUDE_DOCS "Generate build targets for RoSA documentation." ON) option(ROSA_BUILD_DOCS "Build RoSA documentation." OFF) option(ROSA_ENABLE_DOXYGEN "Use doxygen to generate RoSA API documentation." OFF) option(ROSA_ENABLE_SPHINX "Use Sphinx to generate RoSA documentation." OFF) set(ROSA_LOG_LEVEL "" CACHE STRING "Level of logging to be used.") set(ROSA_INCLUDE_APPS "" CACHE STRING "Generate build targets for the defined RoSA applications.") # All options referred to from HandleROSAOptions have to be specified # BEFORE this include, otherwise options will not be correctly set on # first cmake run include(config-ix) include(HandleROSAOptions) # Configure the ROSA configuration header file. if( NOT ROSA_LOG_LEVEL STREQUAL "") if( ${ROSA_LOG_LEVEL} EQUAL 5 ) set(ROSA_LOG_LEVEL_INT -1) else() set(ROSA_LOG_LEVEL_INT ${ROSA_LOG_LEVEL}) endif() else() set(ROSA_LOG_LEVEL_INT -1) endif() configure_file( ${ROSA_MAIN_INCLUDE_DIR}/rosa/config/rosa_config.h.cmake ${ROSA_INCLUDE_DIR}/rosa/config/rosa_config.h ) +# Handling modules +configure_file( + ${ROSA_MAIN_MODULE_DIR}/cxxopts/include/cxxopts.hpp + ${ROSA_INCLUDE_DIR}/cxxopts/cxxopts.hpp + COPYONLY +) + # They are not referenced. See set_output_directory(). set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ROSA_EXEC_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${ROSA_LIBRARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${ROSA_LIBRARY_DIR}) # Set include directories set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(${ROSA_INCLUDE_DIR} ${ROSA_MAIN_INCLUDE_DIR}) # Set up YCM set(CMAKE_EXPORT_COMPILE_COMMANDS 1) message("Set compilation_database_folder in .ymc_extra_conf.py in the source directory if you want to use YMC.") # Add parts of the project include(AddROSATools) add_subdirectory(lib) add_subdirectory(apps) +add_subdirectory(modules) if( ROSA_INCLUDE_TOOLS ) add_subdirectory(tools) endif() if( ROSA_INCLUDE_EXAMPLES ) add_subdirectory(examples) endif() if( ROSA_INCLUDE_DOCS ) add_subdirectory(docs) endif() #TODO: install? # Print summary set(ROSA_ENABLE_ASSERTIONS_STR "OFF") if( ROSA_ENABLE_ASSERTIONS_INT EQUAL 1 ) set(ROSA_ENABLE_ASSERTIONS_STR "ON") endif() set(LOG_LEVEL_STR "disabled") if( NOT ROSA_LOG_LEVEL STREQUAL "" ) if( ${ROSA_LOG_LEVEL} EQUAL 0 ) set(LOG_LEVEL_STR "ERROR") elseif( ${ROSA_LOG_LEVEL} EQUAL 1 ) set(LOG_LEVEL_STR "WARNING") elseif( ${ROSA_LOG_LEVEL} EQUAL 2 ) set(LOG_LEVEL_STR "INFO") elseif( ${ROSA_LOG_LEVEL} EQUAL 3 ) set(LOG_LEVEL_STR "DEBUG") elseif( ${ROSA_LOG_LEVEL} EQUAL 4 ) set(LOG_LEVEL_STR "TRACE") elseif( ${ROSA_LOG_LEVEL} EQUAL 5 ) set(LOG_LEVEL_STR "disabled") else() set(LOG_LEVEL_STR "invalid") endif() endif() message(STATUS "\n====================| Build Summary |====================" "\n" "\nRoSA version: ${PACKAGE_VERSION}" "\n" "\nBuild type: ${CMAKE_BUILD_TYPE}" "\nAssertions: ${ROSA_ENABLE_ASSERTIONS_STR}" "\nClang-tidy: ${ROSA_ENABLE_CLANG_TIDY}" "\nClang-format: ${ROSA_INCLUDE_CLANG_FORMAT}" "\nLog level: ${LOG_LEVEL_STR}" "\n" "\nBuild apps: ${ROSA_INCLUDE_APPS}" "\nBuild tools: ${ROSA_INCLUDE_TOOLS}" "\nBuild examples: ${ROSA_INCLUDE_EXAMPLES}" "\nInclude docs: ${ROSA_INCLUDE_DOCS}" "\nBuild docs: ${ROSA_BUILD_DOCS}" "\n -enable doxygen: ${ROSA_ENABLE_DOXYGEN}" "\n -enable Sphinx: ${ROSA_ENABLE_SPHINX}" "\n" "\nCC: ${CMAKE_C_COMPILER}" "\nCFLAGS: ${CMAKE_C_FLAGS}" "\nCXX: ${CMAKE_CXX_COMPILER}" "\nCXXFLAGS: ${CMAKE_CXX_FLAGS}" "\nLIBRARIES: ${LD_FLAGS}" "\n" "\nSource directory: ${ROSA_MAIN_SRC_DIR}" "\nBuild directory: ${ROSA_BINARY_DIR}" "\nExecutable path: ${ROSA_EXEC_BINARY_DIR}" "\nLibrary path: ${ROSA_LIBRARY_DIR}" "\nGenerator: ${CMAKE_GENERATOR}" "\n" "\n===========================================================\n") diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 813fd09..d00c89d 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,2 +1,7 @@ +# Allow exceptions by removing restricting flag. +if ( ROSA_COMPILER_IS_GCC_COMPATIBLE ) + remove("-fno-exceptions" CMAKE_CXX_FLAGS) +endif() + # Add the different subdirectories ADDALLSUBDIRS() diff --git a/cmake/modules/HandleROSAOptions.cmake b/cmake/modules/HandleROSAOptions.cmake index 6c89812..e569892 100644 --- a/cmake/modules/HandleROSAOptions.cmake +++ b/cmake/modules/HandleROSAOptions.cmake @@ -1,214 +1,221 @@ # This CMake module is responsible for interpreting the user defined ROSA_ # options and executing the appropriate CMake commands to realize the users' # selections. include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) if ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) set(ROSA_COMPILER_IS_CLANG ON) # Enforce minimum version. if ( CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.9.0" ) message(FATAL_ERROR "Insufficient LLVM version") endif() else() set(ROSA_COMPILER_IS_CLANG OFF) endif() if( CMAKE_COMPILER_IS_GNUCXX ) set(ROSA_COMPILER_IS_GCC_COMPATIBLE ON) elseif( MSVC ) set(ROSA_COMPILER_IS_GCC_COMPATIBLE OFF) elseif( ROSA_COMPILER_IS_CLANG ) set(ROSA_COMPILER_IS_GCC_COMPATIBLE ON) else() message(FATAL_ERROR "Unexpected C++ compiler!") endif() function(append value) foreach(variable ${ARGN}) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) endforeach(variable) endfunction() function(append_if condition value) if (${condition}) foreach(variable ${ARGN}) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) endforeach(variable) endif() endfunction() +function(remove value) + foreach(variable ${ARGN}) + string(REPLACE "${value}" "" NEWVAR "${${variable}}") + set(${variable} "${NEWVAR}" PARENT_SCOPE) + endforeach(variable) +endfunction() + # NDEBUG on non-Debug builds append("-DNDEBUG" CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) # Assertions are always enabled on Debug builds, this is interesting for # non-Debug builds. if( NOT "${uppercase_CMAKE_BUILD_TYPE}" STREQUAL "DEBUG" AND ${ROSA_ENABLE_ASSERTIONS} ) # MSVC doesn't like _DEBUG on release builds. See PR 4379. if( NOT MSVC ) add_definitions( -D_DEBUG ) set(ENABLE_ASSERTIONS "1") else() set(ENABLE_ASSERTIONS "0") endif() # On non-Debug builds we NDEBUG, but in this case we need to undefine it: add_definitions(-UNDEBUG) # Also remove /D NDEBUG to avoid MSVC warnings about conflicting defines. foreach (flags_var_to_scrub CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) string (REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") endforeach() endif() set(ROSA_ENABLE_ASSERTIONS_INT -1) if( "${uppercase_CMAKE_BUILD_TYPE}" STREQUAL "DEBUG" OR ${ROSA_ENABLE_ASSERTIONS} ) set(ROSA_ENABLE_ASSERTIONS_INT 1) endif() if( ROSA_COMPILER_IS_GCC_COMPATIBLE ) append("-Wall -Wextra -Wdocumentation -Werror" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) append_if(ROSA_ENABLE_PEDANTIC "-pedantic -Wno-long-long" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) append("-fno-rtti -fno-exceptions" CMAKE_CXX_FLAGS) append("-Wno-c++1z-compat" CMAKE_CXX_FLAGS) check_cxx_compiler_flag("-std=c++17" CXX_SUPPORTS_CXX17) if ( CXX_SUPPORTS_CXX17 ) append("-std=c++17" CMAKE_CXX_FLAGS) else() message(FATAL_ERROR "RoSA requires C++17 support but the '-std=c++17' flag isn't supported.") endif() check_c_compiler_flag("-std=c11" C_SUPPORTS_C11) if ( C_SUPPORTS_C11 ) append("-std=c11" CMAKE_C_FLAGS) else() message(FATAL_ERROR "RoSA requires C11 support but the '-std=c11' flag isn't supported.") endif() elseif ( MSVC ) if (ROSA_COMPILER_IS_CLANG) # Remove/adjsut incorrect flags that might be generated automatically. foreach (flags_var_to_scrub CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) string (REGEX REPLACE "(^| )-Wall($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") string (REGEX REPLACE "(^| )-frtti($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") string (REGEX REPLACE "(^| )-fexceptions($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") string (REGEX REPLACE "(^| )-fno-inline($| )" " -Ob0 " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") string (REGEX REPLACE "(^| )-gline-tables-only($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") message("${flags_var_to_scrub}: ${${flags_var_to_scrub}}") endforeach() endif() # Flags matching GCC-compatible "-Wall -Wextra -Werror". # NOTE: Do not use -Wall for MSVC because it generates silly warning (e.g., # warnings about things in external libraries), use -W4 instead. append("/W4 /WX" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) if ( ROSA_COMPILER_IS_CLANG ) # GCC-compatible flags without corresponding MSVC options. append("-Wdocumentation" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) # The flag "-pedantic" is not in the MSVC flavour. append_if(ROSA_ENABLE_PEDANTIC "-Wno-long-long" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() # Flags matching GCC-compatible "-fno-rtti". append("/GR-" CMAKE_CXX_FLAGS) # NOTE: We cannot disable unwind semantics (i.e., -fno-exceptions) for Windows # libraries... append("/EHsc" CMAKE_CXX_FLAGS) if ( ROSA_COMPILER_IS_CLANG ) append("-Wno-c++1z-compat" CMAKE_CXX_FLAGS) # MSVC puts there some garbage which needs to be ignored... append("-Qunused-arguments" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() if ( ROSA_COMPILER_IS_CLANG ) check_cxx_compiler_flag("-Xclang -std=c++17" CXX_SUPPORTS_CXX17) if ( CXX_SUPPORTS_CXX17 ) append("-Xclang -std=c++17" CMAKE_CXX_FLAGS) else() message(FATAL_ERROR "RoSA requires C++17 support but the '-std=c++17' flag isn't supported.") endif() check_c_compiler_flag("-Xclang -std=c11" C_SUPPORTS_C11) if ( C_SUPPORTS_C11 ) append("-Xclang -std=c11" CMAKE_C_FLAGS) else() message(FATAL_ERROR "RoSA requires C11 support but the '-std=c11' flag isn't supported.") endif() else () check_cxx_compiler_flag("/std:c++17 /permissive-" CXX_SUPPORTS_CXX17) if ( CXX_SUPPORTS_CXX17 ) append("/std:c++17 /permissive-" CMAKE_CXX_FLAGS) else() message(FATAL_ERROR "RoSA requires C++17 support but the '/std:c++17 /permissive-' flags aren't supported.") endif() message(WARNING "RoSA is supposed to use C11 code in C files, which MSVC might not fully support.") # Make MSVC ignore warning C4514 about unreferenced inline functions. append("/nowarn:4514") # Make MSVC ignore warning LNK4221 in libraries. The warning is caused by # empty object files that correspond to the empty .cpp files, which we have # for generating compile database entries for all header files. append("/IGNORE:4221" CMAKE_STATIC_LINKER_FLAGS) endif() endif() if( ROSA_ENABLE_CLANG_TIDY ) # Set clang-tidy arguments, use the same for both C and CXX. set(CMAKE_C_CLANG_TIDY "${CLANGTIDY_EXECUTABLE};-header-filter=.*") set(CMAKE_CXX_CLANG_TIDY ${CMAKE_C_CLANG_TIDY}) # Apply suggested fixes if requested. append_if(ROSA_CLANG_TIDY_FIX ",-fix" CMAKE_C_CLANG_TIDY CMAKE_CXX_CLANG_TIDY) endif( ROSA_ENABLE_CLANG_TIDY ) if( ROSA_INCLUDE_APPS STREQUAL "") message(STATUS "Include all apps by default.") SUBDIRLIST(ROSA_INCLUDE_APPS ${ROSA_MAIN_SRC_DIR}/apps) else() message(STATUS "Include apps: ${ROSA_INCLUDE_APPS}.") endif() diff --git a/docs/Dev.rst b/docs/Dev.rst index 78ed678..f4bdda9 100755 --- a/docs/Dev.rst +++ b/docs/Dev.rst @@ -1,373 +1,387 @@ ============================= Developing the RoSA Framework ============================= .. contents:: :local: This document provides information that might be useful for contributing to RoSA. Please also consult :doc:`Build`. .. _Dev_Source_Directory: The Source Directory ==================== The source directory consists of the following subdirectories: `cmake` Contains files used for configuring the `CMake Project`_. `docs` Contains `Documentation`_-related files. `examples` Contains `Examples`_ on using the public API. `include/rosa` Contains the RoSA public API -- that is the interface of RoSA `Libraries`_. The directory `include` is to be used as include directory and RoSA header files are to be included in C++ sources as `"rosa/"`. `lib` Contains the implementation of the RoSA public API -- that is the implementation of RoSA `Libraries`_. `apps` Contains `Apps`_ based on RoSA features. `tools` Contains `Tools`_ based on RoSA features. +`modules` + Contains third-party `Modules`_ that are not part of RoSA but used by `Apps`_ + Software Sources ================ The section describes the `Logical Structure`_ of the software sources and what `Coding Standards`_ are supposed to be followed for the implementation. Logical Structure ----------------- Various features provided by RoSA are sorted into different `Libraries`_. `Examples`_ , `Apps`_, and `Tools`_ using those `Libraries`_ are separated from the implementation of the RoSA features into different directories. +`Examples`_ , `Apps`_, and `Tools`_ may also use third-party libraries, +called `Modules`_. Libraries ~~~~~~~~~ The framework consists of separate libraries providing different features. The public interfaces for RoSA libraries are defined in `include/rosa`, while corresponding implementation is in `lib`. Each library has its own subdirectory in the mentioned directories. RoSA provides the following libraries: `config` Provides information on the configuration used to build the framework, e.g., version number, log level, assertions, and debugging. `support` Provides general features -- template metaprograms dealing with types, for instance -- for implementing other libraries. `core` Provides the basic RoSA features, like systems managing agents passing messages. `agent` Provides features to be used for implementing agents. `deluxe` Provides a somewhat more modular interface for defining systems with RoSA. .. _Library_Dependencies: Dependencies '''''''''''' The following table summarizes dependencies among libraries. A marking in a row denotes that the library in the beginning of the row depends on the library in the head of the given column. +---------+--------+---------+------+--------+-------+ | | config | support | core | deluxe | agent | +=========+========+=========+======+========+=======+ | config | | | | | | +---------+--------+---------+------+--------+-------+ | support | | | | | | +---------+--------+---------+------+--------+-------+ | core | | × | | | | +---------+--------+---------+------+--------+-------+ | deluxe | | | × | | | +---------+--------+---------+------+--------+-------+ | agent | | | | | | +---------+--------+---------+------+--------+-------+ Examples ~~~~~~~~ Some simple samples are provided in `examples` to demonstrate how to to use different parts of the RoSA API. Apps ~~~~ Apps, actual applications based on the RoSA libraries, are implemented in `apps`. Tools ~~~~~ Tools, programs based on the RoSA libraries and providing standalone functionalities, are implemented in `tools`. +Modules +~~~~~~~ + +This directory contains third-party modules that can be used inside RoSA. These +include: + + * cxxopts: https://github.com/jarro2783/cxxopts + Example usage can be seen in modules/cxxopts/src/example.cpp + .. _Coding_Standards: Coding Standards ---------------- RoSA is implemented in standard *C++17* code. All the software sources are to be written in accordance to the `LLVM Coding Standards`_. .. _nortti-noexceptions: Feature Restrictions ~~~~~~~~~~~~~~~~~~~~ Pay attention `not to use RTTI and Exceptions`_. Those features are disabled in the CMake project. Documentation Comments ~~~~~~~~~~~~~~~~~~~~~~ It is important to remember to document source code using `doxygen comments`_ as `API Documentation`_ is generated directly from sources. Note that the syntax of documentation comments is checked during compilation -- at least when using a GCC-compatible compiler. Further, check :ref:`Doxygen warnings ` as issues not being detected by the compiler may be found when actually generating the documentation. Whenever you work on a source file, make sure your name is in the author-list defined in the header comment of the file. Each author should be defined with a separate `\\author` command so that recent authors come first. Authors not participating in further development of a file anymore may be marked with the period of their contribution. If declarations belonging to a namespace are spread to more than one source files, document the namespace in a separate `namespace.h` in the directory belonging to the library. Otherwise, document the namespace in the only file in which entities of the namespace are declared. Header Files ~~~~~~~~~~~~ Follow the recommendations on public and private header files and the usage of `#include` from the `LLVM Coding Standards`_. Use `.h` and `.hpp` extensions to indicate the content of the header file: * header files containing any *definition* -- template or inline definition -- or including another header file with `.hpp` extension have `.hpp` extension; * header files containing only *declarations* and including only header files with `.h` extension have `.h` extension. It may happen that a header file does not need any corresponding implementation in a `.cpp` file. Nevertheless, do create a corresponding `.cpp` file which only includes the header file in this case. That makes sure that the header file is compiled and hence checked for errors, and also a corresponding entry in the compilation database is generated. Checking and Enforcing the Coding Standards ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The tools `clang-tidy `_ and `clang-format `_ can be used to check and enforce the coding standards. The two tools are integrated into the CMake project, refer to CMake variables :ref:`ROSA_ENABLE_CLANG_TIDY ` and :ref:`ROSA_INCLUDE_CLANG_FORMAT `. Note that there may be situations when `clang-tidy` checks result in false positives -- for example, for some cases of the order of `#include` directives. One can order `clang-tidy` to suppress warnings for a line of code by marking that line with:: // NOLINT It may be preferred to diverge from the standard formatting -- for example for the sake of readability of static definition of arrays following some structure. One can disable `clang-format` for some lines of code by designating a range with two special comments as:: // clang-format off ... clang-format is disabled here ... // clang-format on Documentation ============= The RoSA Framework is delivered with two kinds of documentation: `General Documentation`_ and `API Documentation`_, generation of both of which is integrated into the CMake project. References between the two documentations are relative addresses corresponding to the directory structure of the :ref:`generated documentation `. General Documentation --------------------- General documentation is written as `reStructuredText `_ compiled with `Sphinx `_. For build integration, refer to the CMake variable :ref:`ROSA_ENABLE_SPHINX `. Documentation files are located in `docs` with extension `.rst`. The main page of the documentation is `docs/index.rst`. Configuration for building the documentation is `docs/conf.py`. The directory `docs/CommandGuide` contains documentation for each separate tool. Those pages are included in the HTML documentation via `docs/CommandGuide/index.rst`. Moreover, man pages can be generated from those tool documentation pages. API Documentation ----------------- API documentation is directly generated from sources with `Doxygen `_. For build integration, refer to the CMake variable :ref:`ROSA_ENABLE_DOXYGEN `. The main page used for the API documentation is `docs/doxygen-mainpage.dox`. Configuration for generating the API documentation is `docs/doxygen.cfg.in`. .. _CMake Project: Managing the CMake Project ========================== This section briefly summarizes when and how to modify CMake files during the development process. No general discussion on CMake features is provided here. When modifying `Documentation`_, no need to update the CMake files. Software -------- One needs to modify the CMake files only if source files are to be added or removed from the project. Here follows some typical scenarios. Source Files ~~~~~~~~~~~~ Each library and executable target has its own directory and its own definition as a file called `CMakeLists.txt` in that directory. When adding or removing a source file -- both headers and `.cpp` files -- to a library or executable, locate the corresponding `CMakeLists.txt` file. The file is typically in the same directory where the file to be added or removed is located. Except for header files of the public API, for which the corresponding CMake target is defined in a `lib` subdirectory corresponding to the library the header files belongs to. Update the source list in the argument of the `add_library` or `add_executable` command in the `CMakeLists.txt`, for libraries and executables, respectively. A library and executable may use features provided by another library. Such a dependency is to be defined in the `CMakeLists.txt` file of the dependent target by using the `ROSA_add_library_dependencies` command. CMake Libraries ~~~~~~~~~~~~~~~ When adding or removing a library, add or remove the corresponding directories from `include` and `lib`. If you have already had generated a build project with CMake, `touch` [#]_ `lib/CMakeLists.txt` to make CMake rescan subdirectories on next build. When defining a new library, the new subdirectory under `lib` needs to contain a `CMakeLists.txt`, which needs to contain at least an `add_library` command defining the name of the library and the source files belonging to it. CMake Executables ~~~~~~~~~~~~~~~~~ When adding or removing an executable, add or remove the corresponding directory from `apps`, `examples`, or `tools`. If you have already had generated a build project with CMake, `touch` `CMakeLists.txt` in the containing directory (like in the case of libraries) to make CMake rescan subdirectories on next build. When defining a new executable, the new subdirectory needs to contain a `CMakeLists.txt`, which needs to contain at least an `add_executable` command defining the name of the executable and the source files belonging to it. .. _Dev Managing Sources: Managing Sources ================ Consider the followings before committing changes to the repository: * your code complies with the `Coding Standards`_ as much as possible; * your code is well documented; * your code is not bloated with unusued code and/or comments; * your changes do not break building and executing the framework: * test all of the supported platforms if possible, * look into the generated documentation if you have edited `General Documentation`_; * you do not pollute the repository with unused and generated files. When committing changes to the repository, provide a concise log message with your commit. Miscellaneous Concerns ====================== Using YCM --------- If you happen to use `YCM `_, just make a copy of the provided `ycm_extra_conf.py.template` file as `.ycm_extra_conf.py` in the RoSA source directory, and set the following two variables in it: `compilation_database_folder` the absolute path of your build directory `extra_system_include_dirs` any system include directory which might not be searched by `libclang` [#]_. You probably want compile with Clang if you use YCM, so run CMake with environment variables `CC=clang` and `CXX=clang++` set. Also note that header files in the `include` directory are compiled for YCM with the compiler flags of a corresponding source file in the `lib` directory, if any. Header files in other locations are supposed to have a corresponding source file in the same directory. Notes ~~~~~ * If the project's include directory (`include/rosa`) would ever be changed, then the YCM configuration file needs to be adjusted accordingly. .. rubric:: Footnotes .. [#] Set the last modified time of the file to the current time. .. [#] See: https://github.com/Valloric/YouCompleteMe/issues/303; use the following command to figure out the used system directories: echo | clang -std=c++11 -v -E -x c++ - .. _`LLVM Coding Standards`: http://llvm.org/docs/CodingStandards.html .. _`not to use RTTI and Exceptions`: http://llvm.org/docs/CodingStandards.html#do-not-use-rtti-or-exceptions .. _`doxygen comments`: http://llvm.org/docs/CodingStandards.html#doxygen-use-in-documentation-comments diff --git a/examples/agent-functionalities/agent-functionalities.cpp b/examples/agent-functionalities/agent-functionalities.cpp index 7e4cf9b..0266565 100644 --- a/examples/agent-functionalities/agent-functionalities.cpp +++ b/examples/agent-functionalities/agent-functionalities.cpp @@ -1,109 +1,190 @@ //===-- examples/agent-functionalities/agent-functionalities.cpp *-- C++-*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file examples/agent-functionalities/agent-functionalities.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on defining \c rosa::Agent instances using /// \c rosa::agent::Functionality object as components. /// //===----------------------------------------------------------------------===// +// Make sure M_PI is available, needed for _WIN32 +#define _USE_MATH_DEFINES +#include + #include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/FunctionAbstractions.hpp" +#include "rosa/agent/RangeConfidence.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::terminal; +// We use pi as float rather than double, which M_PI is. +constexpr float Pi = (float) M_PI; + /// A dummy wrapper for testing \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c rosa::MessagingSystem, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special \c rosa::Agent with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; private: enum class Categories { Bad, Normal, Good }; static const std::map CategoryNames; History H; Confidence C; RangeAbstraction A; + PartialFunction L; + RangeConfidence RCL; + RangeConfidence RCS; public: void handler(Tick, uint8_t V) noexcept { // Record \p V to the \c rosa::agent::History, then print state info. H << V; ASSERT(H.entry() == V); // Sanity check. LOG_INFO_STREAM << "\nNext value: " << PRINTABLE(V) << ", confidence: " << C(H) - << ", category: " << CategoryNames.at(A(H.entry())) << '\n'; + << ", category: " << CategoryNames.at(A(H.entry())) + << ", partial: " << int(L(H.entry())) + << ", range-confidence-linear: "; + + std::map res_lin = RCL(H.entry()); + for (auto i : res_lin){ + LOG_INFO_STREAM << " " << CategoryNames.at(i.first) + << " " << i.second << "," ; + } + LOG_INFO_STREAM << " range-confidence-sine: "; + std::map res_sine = RCS(H.entry()); + for (auto i : res_sine){ + LOG_INFO_STREAM << " " << CategoryNames.at(i.first) + << " " << i.second << "," ; + } + LOG_INFO_STREAM << '\n'; + } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, THISMEMBER(handler)), H(), C(5, 20, 1), A({{{(uint8_t)10, (uint8_t)14}, Categories::Normal}, {{(uint8_t)15, (uint8_t)17}, Categories::Good}, {{(uint8_t)18, (uint8_t)19}, Categories::Normal}}, - Categories::Bad) {} + Categories::Bad), + L({{{0, 2}, std::make_shared>(0, 1)}, + {{2, 4}, std::make_shared>(2, 0)}, + {{4, 6}, std::make_shared>(6, -1)}}, + 0), + RCL({ + {Categories::Bad, PartialFunction({ + {{0.f, 3.f}, std::make_shared> + (0.f, 1.f/3)}, + {{3.f, 6.f}, std::make_shared> + (1.f, 0.f)}, + {{6.f, 9.f}, std::make_shared> + (3.f, -1.f/3)}, + },0)}, + {Categories::Normal, PartialFunction({ + {{6.f, 9.f}, std::make_shared> + (-2.f, 1.f/3)}, + {{9.f, 12.f}, std::make_shared> + (1.f, 0.f)}, + {{12.f, 15.f}, std::make_shared> + (5.f, -1.f/3)}, + },0)}, + {Categories::Good, PartialFunction({ + {{12.f, 15.f}, std::make_shared> + (-4.f, 1.f/3)}, + {{15.f, 18.f}, std::make_shared> + (1.f, 0.f)}, + {{18.f, 21.f}, std::make_shared> + (7.f, -1.f/3)}, + },0)} + }), + RCS({ + {Categories::Bad, PartialFunction({ + {{0.f, 3.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2, 0.5f)}, + {{3.f, 6.f}, std::make_shared>(1.f, 0.f)}, + {{6.f, 9.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, + },0)}, + {Categories::Normal, PartialFunction({ + {{6.f, 9.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2, 0.5f)}, + {{9.f, 12.f}, std::make_shared>(1.f, 0.f)}, + {{12.f, 15.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, + },0)}, + {Categories::Good, PartialFunction({ + {{12.f, 15.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2, 0.5f)}, + {{15.f, 18.f}, std::make_shared>(1.f, 0.f)}, + {{18.f, 21.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, + },0)} + }, true){} }; const std::map MyAgent::CategoryNames{ {Categories::Bad, "Bad"}, {Categories::Normal, "Normal"}, {Categories::Good, "Good"}}; int main(void) { - LOG_INFO_STREAM << library_string() << " -- " << Color::Red + LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "agent-functionalities example" << Color::Default << '\n'; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); AgentHandle A = SystemTester::createMyAgent(SP, "MyAgent"); - std::vector Vs{4, 5, 6, 7, 9, 10, 11, 13, + std::vector Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 15, 14, 15, 16, 19, 20, 21}; for (auto I = Vs.begin(); I != Vs.end(); ++I) { A.send(MyAgent::Tick::Value, *I); } SystemTester::destroyMyAgent(SP, A); return 0; } diff --git a/examples/deluxe-interface/deluxe-interface.cpp b/examples/deluxe-interface/deluxe-interface.cpp old mode 100755 new mode 100644 index c742cce..78638ff --- a/examples/deluxe-interface/deluxe-interface.cpp +++ b/examples/deluxe-interface/deluxe-interface.cpp @@ -1,320 +1,338 @@ //===-- examples/deluxe-interface/deluxe-interface.cpp ----------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file examples/deluxe-interface/deluxe-interface.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief A simple example on the \c rosa::deluxe::DeluxeContext and related /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include #include #include using namespace rosa; using namespace rosa::deluxe; using namespace rosa::terminal; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << "deluxe-interface example" << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create("Deluxe"); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the last argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. // The first and second sensors do not receive master-input. AgentHandle BoolSensor = C->createSensor("BoolSensor"); AgentHandle IntSensor = C->createSensor("IntSensor"); // This sensor receives master-input and dumps it to \c LOG_INFO_STREAM. const std::string FloatSensorName = "FloatSensor"; AgentHandle FloatSensor = C->createSensor( FloatSensorName, [&FloatSensorName](std::pair I) { LOG_INFO_STREAM << "\n******\n" << FloatSensorName << " master-input " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; }); // This sensor do not receive master-input but produces tuples. using TupleType = DeluxeTuple; AgentHandle TupleSensor = C->createSensor("TupleSensor"); + // + // Check and set execution policy for sensors. + // + LOG_INFO("Execution policies for sensors."); + + LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); + C->setExecutionPolicy(IntSensor, DeluxeExecutionPolicy::decimation(2)); + C->setExecutionPolicy(FloatSensor, DeluxeExecutionPolicy::decimation(2)); + LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); + // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); // All agents below dump their received values to \c LOG_INFO_STREAM on each // triggering. // This agent does not receive master-input and does not produce // master-output. It results in the value it received. const std::string BoolAgentName = "BoolAgent"; using BoolResult = Optional; using BoolHandler = std::function)>; AgentHandle BoolAgent = C->createAgent( BoolAgentName, BoolHandler([&BoolAgentName](std::pair I) -> BoolResult { LOG_INFO_STREAM << "\n******\n" << BoolAgentName << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {I.first}; })); // This agent receives master-input but does not produce master-output. The // agent maintains a state in \c IntAgentOffset. The master-input handler // updates \c IntAgentOffset according to each received (new) value from its // master. The slave-input handler results in the sum of the received value // and the actual value of \c IntAgentOffset. const std::string IntAgentName = "IntAgent"; using IntMasterHandler = std::function)>; using IntResult = Optional; using IntHandler = std::function)>; uint32_t IntAgentOffset = 0; AgentHandle IntAgent = C->createAgent( IntAgentName, // Master-input handler. IntMasterHandler([&IntAgentName, &IntAgentOffset](std::pair I) { LOG_INFO_STREAM << "\n******\n" << IntAgentName << " master-input " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; if (I.second) { IntAgentOffset = I.first; } }), // Slave-input handler. IntHandler([&IntAgentName, &IntAgentOffset](std::pair I) -> IntResult { LOG_INFO_STREAM << "\n******\n" << IntAgentName << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {I.first + IntAgentOffset}; })); // This agent receives master-input and produces master-output. The // master-input handler propagaates each received (new) value to its slave as // master-output. The slave-input handler results in the value it received and // produces no actual master-output. const std::string FloatAgentName = "FloatAgent"; using FloatMasterResult = std::tuple>; using FloatMasterHandler = std::function)>; using FloatResult = std::tuple, Optional>; using FloatHandler = std::function)>; AgentHandle FloatAgent = C->createAgent( FloatAgentName, // Master-input handler. FloatMasterHandler([&FloatAgentName]( std::pair I) -> FloatMasterResult { LOG_INFO_STREAM << "\n******\n" << FloatAgentName << " master-input " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; const auto Output = I.second ? Optional(I.first) : Optional(); return {Output}; }), // Slave-input handler. FloatHandler([&FloatAgentName](std::pair I) -> FloatResult { LOG_INFO_STREAM << "\n******\n" << FloatAgentName << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {{I.first}, {}}; })); // This agent does not receive master-input and does not produce // master-output. It results in the sum of the values it receives in a tuple. const std::string TupleAgentName = "TupleAgent"; using TupleSumResult = Optional>; using TupleHandler = std::function)>; AgentHandle TupleAgent = C->createAgent( TupleAgentName, TupleHandler( [&TupleAgentName](std::pair I) -> TupleSumResult { LOG_INFO_STREAM << "\n******\n" << TupleAgentName << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {std::get<0>(I.first) + std::get<1>(I.first)}; })); + // + // Set execution policies for low-level agents. + // + LOG_INFO("Setting Execution policies for low-level agents."); + + C->setExecutionPolicy(IntAgent, DeluxeExecutionPolicy::awaitAll({0})); + C->setExecutionPolicy(FloatAgent, DeluxeExecutionPolicy::awaitAll({0})); + // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(BoolAgent, 0, BoolSensor, "Bool Sensor Channel"); C->connectSensor(IntAgent, 0, IntSensor, "Int Sensor Channel"); C->connectSensor(FloatAgent, 0, FloatSensor, "Float Sensor Channel"); C->connectSensor(TupleAgent, 0, TupleSensor, "Tuple Sensor Channel"); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); using SingleDoubleOutputType = Optional>; using SingleUInt32OutputType = Optional>; using NoOutputType = Optional; // This agent does not receive master-input but produces master-output for its // slaves at positions `1` and `2` but not for that at position `0`. The agent // maintains a state in \c SumAgentState. The handler increments \c // SumAgentState upon each received (new) `true` value from its slave at // position `0`. Whenever \c SumAgentState has been updated, it is sent to the // slaves at positions `1` and `2`. The handler results in the sum of the // values received from slaves at positions `1`, `2`, and `3`. using SumResult = std::tuple; using SumHandler = std::function, bool>, std::pair, bool>, std::pair, bool>, std::pair, bool>)>; uint32_t SumAgentState = 0; AgentHandle SumAgent = C->createAgent( "Sum Agent", SumHandler([&SumAgentState]( std::pair, bool> I0, std::pair, bool> I1, std::pair, bool> I2, std::pair, bool> I3) -> SumResult { const auto V0 = std::get<0>(I0.first); const auto V1 = std::get<0>(I1.first); const auto V2 = std::get<0>(I2.first); const auto V3 = std::get<0>(I3.first); LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n" << (I0.second ? "" : "") << " bool value: " << V0 << "\n" << (I1.second ? "" : "") << " int value: " << V1 << "\n" << (I2.second ? "" : "") << " float value: " << V2 << "\n" << (I3.second ? "" : "") << " double value: " << V3 << "\n******\n"; if (I0.second && V0) { ++SumAgentState; } const SingleUInt32OutputType MasterOutput = I0.second && V0 ? SingleUInt32OutputType(DeluxeTuple(SumAgentState)) : SingleUInt32OutputType(); const DeluxeTuple Output = {V1 + V2 + V3}; return {{Output}, {}, {MasterOutput}, {MasterOutput}, {}}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); C->connectAgents(SumAgent, 0, BoolAgent, "Bool Agent Channel"); C->connectAgents(SumAgent, 1, IntAgent, "Int Agent Channel"); C->connectAgents(SumAgent, 2, FloatAgent, "Float Agent Channel"); C->connectAgents(SumAgent, 3, TupleAgent, "Tuple Agent Channel"); // // For simulation output, create a logger agent writing the output of the // high-level agent into a log stream. // LOG_INFO("Create a logger agent."); // The agent dumps each received (new) value to \c LOG_INFO_STREAM and // produces nothing; does not receive mater-input and does not produce // master-output. AgentHandle LoggerAgent = C->createAgent("Logger Agent", std::function(std::pair)>( [](std::pair Sum) -> Optional { if (Sum.second) { LOG_INFO_STREAM << "Result: " << Sum.first << "\n"; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, SumAgent, "Sum Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Create some vectors and register them for their corresponding sensors. // std::vector BoolValues(NumberOfSimulationCycles); std::generate(BoolValues.begin(), BoolValues.end(), [i = 0](void) mutable -> bool { return (++i % 4) == 0; }); C->registerSensorValues(BoolSensor, BoolValues.begin(), BoolValues.end()); std::vector IntValues(NumberOfSimulationCycles); std::generate(IntValues.begin(), IntValues.end(), [i = 0](void) mutable { return ++i; }); C->registerSensorValues(IntSensor, IntValues.begin(), IntValues.end()); std::vector FloatValues(NumberOfSimulationCycles); std::generate(FloatValues.begin(), FloatValues.end(), [f = 0.5f](void) mutable { f += 0.3f; return std::floor(f) + 0.5f; }); C->registerSensorValues(FloatSensor, FloatValues.begin(), FloatValues.end()); std::vector TupleValues(NumberOfSimulationCycles); std::generate(TupleValues.begin(), TupleValues.end(), [f1 = 0.f, f2 = 3.14f](void) mutable -> TupleType { f1 += f2; f2 -= f1; return {f1, f2}; }); C->registerSensorValues(TupleSensor, TupleValues.begin(), TupleValues.end()); // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/include/rosa/agent/Abstraction.hpp b/include/rosa/agent/Abstraction.hpp index c9c4fa2..b44b2de 100644 --- a/include/rosa/agent/Abstraction.hpp +++ b/include/rosa/agent/Abstraction.hpp @@ -1,191 +1,238 @@ //===-- rosa/agent/Abstraction.hpp ------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Abstraction.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *abstraction* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_ABSTRACTION_HPP #define ROSA_AGENT_ABSTRACTION_HPP #include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include #include namespace rosa { namespace agent { /// Abstracts values from a type to another one. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class Abstraction : public Functionality { protected: /// Value to abstract to by default. const A Default; - public: /// Creates an instance. /// /// \param Default value to abstract to by default Abstraction(const A Default) noexcept : Default(Default) {} /// Destroys \p this object. ~Abstraction(void) = default; + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// \return true, the default implementation always falls back to the default + /// value + virtual bool isDefaultAt(const T &V) const noexcept{ + (void)V; + return true; + } + /// Abstracts a value from type \p T to type \p A. /// /// \note The default implementation always returns /// \c rosa::agent::Abstraction::Default, hence the actual argument is /// ignored. /// /// \return the abstracted value virtual A operator()(const T &) const noexcept { return Default; } }; /// Implements \c rosa::agent::Abstraction as a \c std::map from a type to /// another one. /// /// \note This implementation is supposed to be used to abstract between /// enumeration types, which is statically enforced. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class MapAbstraction : public Abstraction, private std::map { // Make sure the actual type arguments are enumerations. STATIC_ASSERT((std::is_enum::value && std::is_enum::value), "mapping not enumerations"); // Bringing into scope inherited members. using Abstraction::Default; using std::map::end; using std::map::find; public: /// Creates an instance by initializing the underlying \c std::map. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default MapAbstraction(const std::map &Map, const A Default) noexcept : Abstraction(Default), std::map(Map) {} /// Destroys \p this object. ~MapAbstraction(void) = default; + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// \return true if the Abstraction falls back to the default value + bool isDefaultAt(const T &V) const noexcept override { + const auto I = find(V); + return I == end() ? true : false; + } + /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c rosa::agent::MapAbstraction::Default if the actual argument is not /// associated with anything by the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { const auto I = find(V); return I == end() ? Default : *I; } }; /// Implements \c rosa::agent::Abstraction as a \c std::map from ranges of a /// type to values of another type. /// /// \note This implementation is supposed to be used to abstract ranges of /// arithmetic types into enumerations, which is statically enforced. /// /// \invariant The keys in the underlying \c std::map define valid ranges /// such that `first <= second` and there are no overlapping ranges defined by /// the keys. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class RangeAbstraction : public Abstraction, private std::map, A> { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); - STATIC_ASSERT((std::is_enum::value), "abstracting not to enumeration"); + /// \todo check if this compiles with the definition of abstractions as + /// self-aware properties + //STATIC_ASSERT((std::is_enum::value), "abstracting not to enumeration"); // Bringing into scope inherited members. using Abstraction::Default; using std::map, A>::begin; using std::map, A>::end; using std::map, A>::find; public: /// Creates an instance by Initializing the unserlying \c std::map. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default /// /// \pre Each key defines a valid range such that `first <= second` and /// there are no overlapping ranges defined by the keys. RangeAbstraction(const std::map, A> &Map, const A &Default) : Abstraction(Default), std::map, A>(Map) { // Sanity check. ASSERT(std::all_of( begin(), end(), [this](const std::pair, A> &P) { return P.first.first <= P.first.second && std::all_of(++find(P.first), end(), [&P](const std::pair, A> &R) { // \note Values in \c Map are sorted. return P.first.first < P.first.second && P.first.second <= R.first.first || P.first.first == P.first.second && P.first.second < R.first.first; }); })); } /// Destroys \p this object. ~RangeAbstraction(void) = default; + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// \return true if the Abstraction falls back to the default value + bool isDefaultAt(const T &V) const noexcept override { + auto I = begin(); + bool Found = false; // Indicates if \c I refers to a matching range. + bool Failed = false; // Indicates if it is pointless to continue searching. + while (!Found && !Failed && I != end()) { + if (V < I->first.first) { + // No match so far and \p V is below the next range, never will match. + // \note Keys are sorted in the map. + return true; + } else if (I->first.first <= V && V < I->first.second) { + // Matching range found. + return false; + } else { + // Cannot conclude in this step, move to the next range. + ++I; + } + } + return true; + } + /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c rosa::agent::RangeAbstraction::Default if the actual argument is not /// included in any of the ranges in the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { auto I = begin(); bool Found = false; // Indicates if \c I refers to a matching range. bool Failed = false; // Indicates if it is pointless to continue searching. while (!Found && !Failed && I != end()) { if (V < I->first.first) { // No match so far and \p V is below the next range, never will match. // \note Keys are sorted in the map. Failed = true; } else if (I->first.first <= V && V < I->first.second) { // Matching range found. Found = true; } else { // Cannot conclude in this step, move to the next range. ++I; } } ASSERT(!Found || I != end()); return Found ? I->second : Default; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_ABSTRACTION_HPP diff --git a/include/rosa/agent/FunctionAbstractions.hpp b/include/rosa/agent/FunctionAbstractions.hpp new file mode 100644 index 0000000..9a7127a --- /dev/null +++ b/include/rosa/agent/FunctionAbstractions.hpp @@ -0,0 +1,224 @@ +//===-- rosa/agent/FunctionAbstractions.hpp ---------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/agent/FunctionAbstractions.hpp +/// +/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Definition of *FunctionAbstractions* *functionality*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP +#define ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP + +#include "rosa/agent/Functionality.h" +#include "rosa/agent/Abstraction.hpp" + +#include "rosa/support/debug.hpp" + +#include +#include +#include +#include + +namespace rosa { +namespace agent { + +/// Implements \c rosa::agent::Abstraction as a linear function, +/// y = Coefficient * X + Intercept. +/// +/// \note This implementation is supposed to be used to represent a linear +/// function from an arithmetic domain to an arithmetic range. This is enforced +/// statically. +/// +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template class LinearFunction : + public Abstraction{ + // Make sure the actual type arguments are matching our expectations. + STATIC_ASSERT((std::is_arithmetic::value), + "LinearFunction not arithmetic T"); + STATIC_ASSERT((std::is_arithmetic::value), + "LinearFunction not to arithmetic"); +protected: + /// The Intercept of the linear function + const D Intercept; + /// The Coefficient of the linear function + const D Coefficient; + +public: + /// Creates an instance. + /// + /// \param Intercept the intercept of the linear function + /// \param Coefficient the coefficient of the linear function + LinearFunction(D Intercept, D Coefficient) noexcept + : Abstraction(Intercept), + Intercept(Intercept), + Coefficient(Coefficient) {} + + /// Destroys \p this object. + ~LinearFunction(void) = default; + + /// Checks wether the Abstraction evaluates to default at the given position + /// As LinearFunctions can be evaluated everythwere, this is always false + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// + /// \return false + bool isDefaultAt(const D &V) const noexcept override { + (void)V; + return false; + } + + /// Evaluates the linear function + /// + /// \param X the value at which to evaluate the function + /// + /// \return Coefficient*X + Intercept + virtual R operator()(const D &X) const noexcept override { + return Intercept + X*Coefficient; + } +}; + +/// Implements \c rosa::agent::Abstraction as a sine function, +/// y = Amplitude * sin(Frequency * X + Phase) + Average. +/// +/// \note This implementation is supposed to be used to represent a sine +/// function from an arithmetic domain to an arithmetic range. This is enforced +/// statically. +/// +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template class SineFunction : + public Abstraction{ + // Make sure the actual type arguments are matching our expectations. + STATIC_ASSERT((std::is_arithmetic::value), + "SineFunction not arithmetic T"); + STATIC_ASSERT((std::is_arithmetic::value), + "SineFunction not to arithmetic"); +protected: + /// The frequency of the sine wave + const D Frequency; + /// The Ampiltude of the sine wave + const D Amplitude; + /// The Phase-shift of the sine wave + const D Phase; + /// The y-shift of the sine wave + const D Average; + +public: + /// Creates an instance. + /// + /// \param Frequency the frequency of the sine wave + /// \param Amplitude the amplitude of the sine wave + /// \param Phase the phase of the sine wave + /// \param Average the average of the sine wave + SineFunction(D Frequency, D Amplitude, D Phase, D Average) noexcept + : Abstraction(Average), + Frequency(Frequency), + Amplitude(Amplitude), + Phase(Phase), + Average(Average) {} + + /// Destroys \p this object. + ~SineFunction(void) = default; + + /// Checks wether the Abstraction evaluates to default at the given position + /// As SineFunctions can be evaluated everythwere, this is always false + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// + /// \return false + bool isDefaultAt(const D &V) const noexcept override { + (void)V; + return false; + } + + /// Evaluates the sine function + /// + /// \param X the value at which to evaluate the function + /// \return the value of the sine-function at X + virtual R operator()(const D &X) const noexcept override { + return Amplitude*sin(Frequency * X + Phase) + Average; + } +}; +/// Implements \c rosa::agent::Abstraction as a partial function from a domain +// /to a range. +/// +/// \note This implementation is supposed to be used to represent a partial +/// function from an arithmetic domain to an arithmetic range. This is enforced +/// statically. +/// +/// A partial function is defined as a list of abstractions, where each +/// abstraction is associated a range in which it is defined. These ranges must +/// be mutually exclusive. +/// +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class PartialFunction : public Abstraction { + // Make sure the actual type arguments are matching our expectations. + STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "abstracting not to arithmetic"); + +private: + /// A \c rosa::agent::RangeAbstraction RA is used to represent the association + /// from ranges to Abstractions. + /// This returns the Abstraction that is defined for any given value, or + /// a default Abstraction if no Abstraction is defined for that value. + RangeAbstraction>> RA; + +public: + /// Creates an instance by Initializing the underlying \c Abstraction. + /// + /// \param Map the mapping to do abstraction according to + /// \param Default abstraction to abstract to by default + /// + /// \pre Each key defines a valid range such that `first <= second` and + /// there are no overlapping ranges defined by the keys. + PartialFunction(const std::map, + std::shared_ptr>> &Map, + const R Default) + : Abstraction(Default), + RA(Map, std::shared_ptr> + (new Abstraction(Default))) { + } + + /// Destroys \p this object. + ~PartialFunction(void) = default; + + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// + /// \return false if the value falls into a defined range and the Abstraction + /// defined for that range does not fall back to it's default value. + bool isDefaultAt(const D &V) const noexcept override { + return RA.isDefaultAt(V) ? true : RA(V)->isDefaultAt(V); + } + + /// Searches for an Abstraction for the given value and executes it for that + /// value, if such an Abstraction is found. The default Abstraction is + /// evaluated otherwise. + /// + /// \param V value to abstract + /// + /// \return the abstracted value based on the set mapping + R operator()(const D &V) const noexcept override { + return RA(V)->operator()(V); + } +}; +} // End namespace agent +} // End namespace rosa + +#endif // ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP diff --git a/include/rosa/agent/RangeConfidence.hpp b/include/rosa/agent/RangeConfidence.hpp new file mode 100644 index 0000000..0d8732e --- /dev/null +++ b/include/rosa/agent/RangeConfidence.hpp @@ -0,0 +1,109 @@ +//===-- rosa/agent/RangeConfidence.hpp --------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/agent/RangeConfidence.hpp +/// +/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Definition of *RangeConfidence* *functionality*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_AGENT_RANGECONFIDENCE_HPP +#define ROSA_AGENT_RANGECONFIDENCE_HPP + +#include "rosa/agent/Functionality.h" +#include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/FunctionAbstractions.hpp" + +#include "rosa/support/debug.hpp" + +#include +#include +#include +#include + +namespace rosa { +namespace agent { + +/// Evaluates a map of ID's to Abstractions at a given value and returns the +/// results as a map from ID's to results of the corresponding Abstraction +/// +/// \note This implementation is supposed to be used to abstract ranges of +/// arithmetic types into maps whose values are of another arithmetic type, +/// which is statically enforced. +/// +/// \tparam D type to abstract from +/// \tparam I type the type of the ID's +/// \tparam R type of the range +template +class RangeConfidence : protected Abstraction>, + private std::map>{ + // Make sure the actual type arguments are matching our expectations. + STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "abstracting not to arithmetic"); + +private: + /// Wether to include default results in the result-map or not + bool IgnoreDefaults; + +public: + /// Creates an instance by Initializing the underlying \c Abstraction and + /// \c std::map. + /// + /// \param Abstractions the Abstractions to be evaluated + /// \param IgnoreDefaults wether to include default results in the result-map + /// or not (defaults to false). + RangeConfidence(const std::map> &Abstractions, + bool IgnoreDefaults = false) + : Abstraction>({}), + std::map>(Abstractions), + IgnoreDefaults(IgnoreDefaults){ + } + + /// Destroys \p this object. + ~RangeConfidence(void) = default; + + /// Checks wether all Abstractions evaluate to default at the given position + /// + /// \param V the value at which to check if the functions falls back to it's + /// default value. + /// + /// \return true, if all Abstractions evaluate to default + bool isDefaultAt(const D &V) const noexcept override { + for (auto const& p : ((std::map>)*this)){ + if(!p.second.isDefaultAt(V)) + return false; + } + return true; + } + + /// All Abstractions stored in the underlying \c std::map are evaluated for + /// the given value. Their results are stored in another map, with + /// corresponding keys. + /// If IgnoreDefaults is set, Abstractions that default for that value are not + /// evaluated and inserted into the resulting \c std::map + /// + /// \param V value to abstract + /// + /// \return a \c std::map containing the results of the stored Abstractions, + /// indexable by the key's the Abstractions are associated with + std::map operator()(const D &V) const noexcept override { + std::map ret; + for (auto const& p : ((std::map>)*this)){ + if(!IgnoreDefaults || !p.second.isDefaultAt(V)) + ret.insert(std::pair(p.first, p.second(V))); + } + return ret; + } +}; +} // End namespace agent +} // End namespace rosa + +#endif // ROSA_AGENT_RANGECONFIDENCE_HPP diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp old mode 100755 new mode 100644 index 49923d7..4d54a08 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,1442 +1,1478 @@ //===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *agent* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEAGENT_HPP #define ROSA_DELUXE_DELUXEAGENT_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" +#include "rosa/deluxe/DeluxeExecutionPolicy.h" #include "rosa/deluxe/DeluxeTuple.hpp" #include /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. /// /// \param N name suffix to use #define DASLAVEHANDLERNAME(N) handleSlave_##N /// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. /// /// \param N name suffix to use #define DAMASTERHANDLERNAME(N) handleMaster_##N /// Defines member functions for handling messages from *slaves* in /// \c rosa::deluxe::DeluxeAgent. /// /// \see \c DeluxeAgentInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeAgent::saveInput to do that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DASLAVEHANDLERDEFN(T, N) \ void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, token_size_t Pos, \ T Value) noexcept { \ saveInput(SlaveId, Pos, Value); \ } /// Defines member functions for handling messages from *master* in /// \c rosa::deluxe::DeluxeAgent. /// /// \see \c DeluxeAgentMasterInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeAgent::saveMasterInput to do /// that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DAMASTERHANDLERDEFN(T, N) \ void DAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \ T Value) noexcept { \ saveMasterInput(MasterId, Pos, Value); \ } /// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments. /// /// \see \c DASLAVEHANDLERDEFN /// /// This macro can be used instead of \c DASLAVEHANDLERDEFN if the actual value /// of \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DASLAVEHANDLERDEF(T) DASLAVEHANDLERDEFN(T, T) /// Convenience macro for \c DAMASTERHANDLERDEFN with identical arguments. /// /// \see \c DAMASTERHANDLERDEFN /// /// This macro can be used instead of \c DAMASTERHANDLERDEFN if the actual value /// of \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DAMASTERHANDLERDEF(T) DAMASTERHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DASLAVEHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super /// class \c rosa::Agent with member function defined by \c DASLAVEHANDLERDEFN. /// /// \see \c DASLAVEHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DASLAVEHANDLERREF(N) THISMEMBER(DASLAVEHANDLERNAME(N)) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DAMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super /// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN. /// /// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DAMASTERHANDLERREF(N) THISMEMBER(DAMASTERHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// -/// \invariant All input-related container objects have a size matching -/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding -/// entry for each input. \c rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs -/// matches \c rosa::deluxe::DeluxeAgent::NumberOfInputs. All -/// master-output-related container objects have a size matching \c +/// \invariant There is a compatible *execution policy* set, all input-related +/// container objects have a size matching \c +/// rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding entry +/// for each input. \c rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs matches +/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs. All master-output-related +/// container objects have a size matching \c /// rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs. Types and type-related /// information of input and master-output values are consistent throughout all /// the input-related and master-output-related containers, respectively. The /// actual values in \c rosa::deluxe::DeluxeAgent::InputNextPos and \c /// rosa::deluxe::DeluxeAgent::MasterInputNextPos are valid with respect to the /// corresponding types. No *slave* is registered at more than one input /// position. *Slave* registrations and corresponding reverse lookup /// information are consistent. /// /// \see Definition of \c rosa::deluxe::DeluxeAgent::inv on the class invariant /// /// \note All member functions validate the class invariant as part of their /// precondition. Moreover, non-const functions validate the invariant before /// return as their postcondition. class DeluxeAgent : public Agent { /// Checks whether \p this object holds the class invariant. /// /// \see Invariant of the class \c rosa::deluxe::DeluxeAgent /// /// \return if \p this object holds the class invariant bool inv(void) const noexcept; + /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of + /// \c this object. + std::unique_ptr ExecutionPolicy; + public: /// The type of values produced by \p this object. /// /// That is the types of values \p this object sends to its *master* in a \c /// rosa::deluxe::DeluxeTUple. /// /// \see \c rosa::deluxe::DeluxeAgent::master const Token OutputType; /// Number of inputs processed by \p this object. const size_t NumberOfInputs; /// The type of values \p this object processes from its *master*. /// /// That is the types of values \p this object receives from its *master* in a /// \c rosa::deluxe::DeluxeTuple. /// /// \see \c rosa::deluxe::DeluxeAgent::master const Token MasterInputType; /// Number of outputs produces by \p this object for its *slaves*. /// /// \note This values is equal to \c /// rosa::deluxe::DeluxeAgent::NumberOfInputs. /// /// \see \c rosa::deluxe::DeluxeAgent::slave. const size_t NumberOfMasterOutputs; private: /// Types of input values produced by *slaves* of \p this object. /// /// \note The \c rosa::Token values stored correspond to \c /// rosa::deluxe::DeluxeTuple instances at each argument position. The \c /// rosa::TypeNumber values from the stored \c rosa::Token values match the /// corresponding values in \c rosa::deluxe::DeluxeAgent::InputValues in /// order. /// /// \note The position of a \c rosa::Token in the \c std::vector indicates /// which argument of \p this object's processing function it belongs to. See /// also \c rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::vector InputTypes; /// Indicates which element of an input is expected from any particular /// *slave*. /// /// The *slave* is supposed to send one \c rosa::deluxe::DeluxeTuple value /// element by element in their order of definition. This member field tells /// the element at which position in the tuple should be received next from /// the *slave* at a given position. /// /// \p this object is supposed to be triggered only when input values has been /// received completely, that is all values in the field should hold the value /// `0`. /// /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger /// \c rosa::deluxe::DeluxeAgent::saveInput std::vector InputNextPos; /// Indicates whether any particular input value has been changed since the /// last trigger received from the system. /// /// All the flags are reset to \c false upon handling a trigger and then set /// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new /// input value in \c rosa::deluxe::DeluxeAgent::InputValues. /// /// \note The position of a flag in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. std::vector InputChanged; /// Tells at which position in \c rosa::deluxe::DeluxeAgent::InputValues the /// input from any particular *slave* starts. /// /// \note A value in the vector corresponds to the *slave* at the same /// position and it is the sum of the elements of input values from *slaves* /// at previous positions. /// /// \see \c rosa::deluxe::DeluxeAgent::saveInput const std::vector InputStorageOffsets; /// Stores the actual input values. /// /// \note The types of stored values match the corresponding /// \c rosa::TypeNumber values (in \c rosa::Token in order) in \c /// rosa::deluxe::DeluxeAgent::InputTypes. /// /// \note The position of a value in the \c rosa::AbstractTokenizedStorage /// indicates which element of the tuple of which argument of \p this object's /// processing function it is. See also \c /// rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::unique_ptr InputValues; /// Indicates which element of the master-input is expected from the *master*. /// /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value /// element by element in their order of definition. This member field tells /// the element at which position should be received next. /// /// \p this object is supposed to be triggered only when a complete /// master-input has been received, that is the field should hold the value /// `0`. /// /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger /// \c rosa::deluxe::DeluxeAgent::saveMasterInput token_size_t MasterInputNextPos; /// Indicates whether the input value from the *master* has been changed since /// the last trigger received from the system. /// /// The flag is reset to \c false upon handling a trigger and then set to \c /// true by \c rosa::deluxe::DeluxeAgent::saveMasterInput when storig a new /// input value in \c rosa::deluxe::DeluxeAgent::MasterInputValue. bool MasterInputChanged; /// Stores the actual input value from *master*. /// /// \note The type of the stored value matches the types indicated by \c /// rosa::deluxe::DeluxeAgent::MasterInputType. const std::unique_ptr MasterInputValue; /// Types of output values produced by \p this object for its *slaves*. /// /// That is the types of values \p this object sends to its *slaves* in a \c /// rosa::deluxe::DeluxeTuple. /// /// \note The position of a type in the \c std::vector indicates which /// *slave* of \p this object the type belongs to. See also /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::vector MasterOutputTypes; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeAgent. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c rosa::deluxe::DeluxeAgent::FP using H = std::function; /// Handles trigger from the system. /// /// The actual functions processing *slave* and *master* inputs and generating /// optional output to *master* and *slaves* are captured in a lambda /// expression that is in turn wrapped in a \c std::function object. The /// lambda expression calls the master-input processing function with the /// actual master-input data and sends its result -- if any -- to *slaves* by /// calling \c rosa::deluxe::DeluxeAgent::handleMasterOutputs; then calls the /// input processing function with the actual input data and sends its result /// -- if any -- to *master* by calling \c /// rosa::deluxe::DeluxeAgent::sendToMaster and *slaves* by calling \c /// rosa::deluxe::DeluxeAgent::handleMasterOutputs. Also, all the flags stored /// in \c rosa::deluxe::DeluxeAgent::InputChanged and \c /// rosa::deluxe::DeluxeAgent::MasterInputChanged are reset when the current /// values are processed. The function \c /// rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the /// function object. /// /// \see \c /// rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunctions const H FP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a /// given moment. Optional Master; /// The *slaves* sending input to \p this object. /// /// \note The position of a *slave* in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \note *Slaves* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does have input positions without /// any *slave* associated to them. /// /// \note Reverse lookup information is maintained in /// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with /// the *slaves* stored here. std::vector> Slaves; /// Associates \c rosa::id_t values to corresponding indices of registered /// *slaves*. /// /// \see \c rosa::deluxe::DeluxeAgent::Slaves std::map SlaveIds; /// Tells the unique identifier of the *master* of \p this object, if any /// registered. /// /// \return the unique identifier of the *master* /// /// \pre A *master* is registered for \p this object: \code /// Master /// \endcode id_t masterId(void) const noexcept; /// Tells whether types stored in \c rosa::TypeList \p As match the input /// types of \p this object. /// /// \tparam As \c rosa::TypeList containing types to match against values in /// \c rosa::deluxe::DeluxeAgent::InputTypes /// /// \note Instatiation of the template fails if \p As is not \c /// rosa::TypeList. /// /// \return if types in \p As are instances of \c rosa::deluxe::DeluxeTuple /// and their types match \c rosa::Token values stored in \c /// rosa::deluxe::DeluxeAgent::InputTypes template bool inputTypesMatch(void) const noexcept; /// Tells whether types stored in \c rosa::TypeList \p Ts match the /// master-output types of \p this object. /// /// \tparam Ts \c rosa::TypeList containing types to match against values in /// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes /// /// \note Instatiation of the template fails if \p As is not \c /// rosa::TypeList. /// /// \return if types in \p Ts match \c rosa::Token and in turn \c /// rosa::TypeNumber values stored in \c /// rosa::deluxe::DeluxeAgent::MasterOutputTypes template bool masterOutputTypesMatch(void) const noexcept; /// Gives the current input value for slave position \p Pos. /// /// \tparam Pos slave position to get input value for /// \tparam Ts types of elements of the input value /// \tparam S0 indices for accessing elements of the input value /// /// \note The arguments provide types and indices statically as template /// arguments \p Ts... \p S0..., respectively, so their actual values are /// ignored. /// /// \return current input value for slave position \p Pos /// /// \pre Statically, the provided indices \p S0... match the length of \p /// Ts...: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Pos is a valid slave position and type arguments /// \p Ts... match the corresponding input value: \code /// Pos < NumberOfInputs && DeluxeTuple::TT == InputTypes[Pos] /// \endcode template DeluxeTuple prepareInputValueAtPos(TypeList, Seq) const noexcept; /// Gives an \c std::tuple containing the current input values and their /// change flags so that they can be used for the processing function. /// /// \tparam As types of the input values /// \tparam S0 indices for accessing input values and their change flags /// /// \note The only argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return current input values and their change flags prepared for invoking /// the processing function with them /// /// \pre Statically, all type arguments \p As... are instances of \c /// rosa::deluxe::DeluxeTuple and the provided indices \p S0... match the /// length of \p As...: \code /// TypeListAllDeluxeTuple>::Value && /// sizeof...(As) == sizeof...(S0) /// \endcode Dynamically, type arguments \p As... match the input types of \p /// this object: \code /// inputTypesMatch>() /// \endcode template std::tuple...> prepareCurrentInputs(Seq) const noexcept; /// Invokes a processing function matching the input, output, and /// master-output types of \p this object with actual arguments provided in a /// \c std::tuple. /// /// \note \p Args providing the actual arguments for \p F is to be created by /// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs. /// /// \tparam T output type of the processing function /// \tparam Ts types of master-output values of the processing function /// \tparam As types of inputs for the processing function /// \tparam S0 indices starting with `0` for extracting actual arguments from /// \p Args /// /// \param F the processing function to invoke /// \param Args the actual arguments to invoke \p F with /// /// \note The last argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return the result of \p F for actual arguments \p Args /// /// \pre The provided sequence of indices \p S0... constitutes a proper /// sequence for extracting all actual arguments for /// \p F from \p Args: \code /// sizeof...(As) == sizeof...(S0) /// \endcode template static std::tuple, Optional...> invokeWithTuple(std::function, Optional...>( std::pair...)> F, const std::tuple...> Args, Seq) noexcept; /// Handles a master-output value for a particular *slave* position. /// /// \p Value is a \c rosa::Optional resulted by a processing function and /// contains a master-output value for the *slave* at position \p Pos. The /// function takes the master-output value and sends its actual value, if any, /// to the corresponding *slave*. /// /// \note A master-output of type \c rosa::deluxe::EmptyDeluxeTuple indicates /// no actual output and hence no message is generated for a position whose /// corresponding master-output type is \c rosa::deluxe::EmptyDeluxeTuple. /// /// \note The function provides position-based implementation for \c /// rosa::deluxe::DeluxeAgent::handleMasterOutputs. /// /// \tparam Pos the position of the master-output to send \p Value for /// \tparam Ts types of elements in \p Value /// /// \param Value \c rosa::deluxe::DeluxeTuple resulted by the processing /// function for *slave* position \p Pos /// /// \pre \p Pos is a valid master-output position and \p Value matches the /// master-output type of \p this object at position \p Pos: \code /// Pos < NumberOfMasterOutputs && /// DeluxeTuple::TT == MasterOutputTypes[Pos] /// \endcode template void handleMasterOutputAtPos(const Optional> &Value) noexcept; /// Handles master-output values from \p Output. /// /// \p Output is a \c std::tuple resulted by a processing function and /// contains master-output values starting at position \p Offset. The function /// takes master-output values and sends each actual value to the /// corresponding *slave*. /// /// \tparam Offset index of the first master-output value in \p Output /// \tparam Ts output types stored in \p Output /// \tparam S0 indices starting with `0` for extracting master-output values /// from \p Output /// /// \note Instantiation fails if any of the type arguments \p Ts... starting /// at position \p Offset is not an instance of \c rosa::deluxe::DeluxeTuple /// or the number of types \p Ts... is not consistent with the other template /// arguments. /// /// \param Output \c std::tuple resulted by a processing function /// /// \pre Statically, type arguments \p Ts... starting at position \p Offset /// are instances of \c rosa::deluxe::DeluxeTuple and the number of types \p /// Ts... is consistent with the other template arguments: \code /// TypeListAllDeluxeTuple< /// typename TypeListDrop>::Type>::Value && /// sizeof...(Ts) == Offset + sizeof...(S0) /// \endcode Dynamically, \p Output matches the master-output types \p this /// object was created with and the provided sequence of indices \p S0... /// constitues a proper sequence for extracting all master-output values from /// \p Output: \code /// masterOutputTypesMatch>::Type>() && /// sizeof...(S0) == NumberOfMasterOutputs /// \endcode template void handleMasterOutputs(const std::tuple...> &Output, Seq) noexcept; /// Wraps processing functions into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeAgent::FP /// /// \note The function cannot be const qualified because the lambda /// expression defined in it needs to capture \p this object by a non-const /// reference /// /// \tparam MTs types of elements of master-input processed by \p MF /// \tparam T type of output /// \tparam Ts types of master-output values /// \tparam As types of input values /// \tparam S0 indices for accessing master-input values /// /// \note Instantiation fails if any of the type arguments \p T, \p Ts..., /// and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple. /// /// \param MF function processing master-input and generating output /// \param F function processing inputs and generating output /// /// \note The last argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates /// that \p this object does not receive master-input, \p MF is never called /// if \p MTs is empty. /// /// \return trigger handler function based on \p F and \p MF /// /// \pre Statically, type arguments \p T, \p Ts..., and \p As... are /// instances of \c rosa::deluxe::DeluxeTuple and the indices match /// master-input elements: \code /// TypeListAllDeluxeTuple>::Value && /// sizeof...(MTs) == sizeof...(S0) /// \endcode Dynamically, template arguments \p MTs..., \p T, \p Ts..., and /// \p As... match the corresponding types \p this object was created with: /// \code /// MasterInputType == DeluxeTuple::TT && OutputType == T::TT && /// inputTypesMatch>() && /// masterOutputTypesMatch>() /// \endcode template H triggerHandlerFromProcessingFunctions( std::function...>( std::pair, bool>)> &&MF, std::function< std::tuple, Optional...>(std::pair...)> &&F, Seq) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// The function \p F generates a \c std::tuple of values: the first value is /// the output for the *master* and the rest is for the *slaves*. All output /// generated by the function is optional as an agent may decide not to output /// anything at some situation. /// /// \todo Enforce \p F and \p MF do not potentially throw exception. /// /// \tparam MT type of master-input handled by \p MF /// \tparam T type of output of \p F /// \tparam Ts type of master-output values of \p F and \p MF /// \tparam As types of input values of \p F /// /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple or the /// number of inputs and master-outputs are not equal. /// /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed /// object does not receive master-input. Similarly, if any of \p Ts... is \c /// rosa::deluxe::EmptyDeluxeTuple, the constructed object does not generated /// master-output for the corresponding *slave* position. /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param MF function to process master-input values and generate /// master-output with /// \param F function to process input values and generate output and /// master-output with /// /// \pre Statically, all the type arguments \p MT, \p T, \p Ts..., and \p /// As... are instances of \c rosa::deluxe::DeluxeTuple, with \p T and \p /// As... containing at least one element, and the number of input and /// master-output types are equal: \code /// TypeListAllDeluxeTuple::Value && /// T::Length > 0 && (true && ... && As::Length > 0) && /// sizeof...(Ts) == sizeof...(As) ///\endcode /// Dynamically, the instance is created as of kind \c /// rosa::deluxe::atoms::AgentKind: \code /// Kind == rosa::deluxe::atoms::AgentKind /// \endcode /// /// \see \c rosa::deluxe::DeluxeTuple template >::Value && (T::Length > 0) && (true && ... && (As::Length > 0)) && sizeof...(Ts) == sizeof...(As)>> DeluxeAgent( const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Destroys \p this object. ~DeluxeAgent(void) noexcept; + /// Returns the current execution policy of \p this object. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeAgent::setExecutionPolicy() is not called and \p this + /// object is not destroyed. + /// + /// \return \c rosa::deluxe::DeluxeAgent::ExecutionPolicy + const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; + + /// Sets the current execution policy of \p this object to \p EP. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note \p EP is set only if it can handle \p this object. + /// + /// \param EP the new execution policy for \p this object + /// + /// \return if \p EP was successfully set for \p this object. + bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; + /// The *master* of \p this object, if any is registered. /// /// \see \c rosa::deluxe::DeluxeAgent::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \note Any call to \c rosa::deluxe::DeluxeAgent::registerMaster should be /// paired with a corresponding call of \c /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that /// input/output types of master and slave matches. /// /// \param _Master the *master* to register /// /// \pre \p _Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Tells the types of values consumed from the *slave* at a position. /// /// That is the type of values \p this object expect to be sent to it in a \c /// rosa::deluxe::DeluxeTuple by its *slave* registered at position \p Pos. /// /// \see \c rosa::deluxe::DeluxeAgent::slave /// /// \param Pos position of *slave* /// /// \return \c rosa::Token representing the types of values consumed from /// the *slave* at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode Token inputType(const size_t Pos) const noexcept; /// Tells the types of values produced for the *slave* at a position. /// /// That is the types of values \p this object potentially sends in a \c /// rosa::deluxe::DeluxeTuple to its *slave* registered at position \p Pos. /// /// \see \c rosa::deluxe::DeluxeAgent::slave /// /// \param Pos position of *slave* /// /// \return \c rosa::Token representing the types of values produced for /// the *slave* at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfMasterOutputs /// \endcode Token masterOutputType(const size_t Pos) const noexcept; /// The *slave* of \p this object registered at a position, if any. /// /// \see \c rosa::deluxe::DeluxeAgent::registerSlave /// /// \param Pos position of *slave* /// /// \return the *slave* registered for \p this object at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode Optional slave(const size_t Pos) const noexcept; /// Registers a *slave* for \p this object at a position. /// /// The new *slave* is registered by overwriting the reference to any already /// registered *slave* at position \p Pos. One can clear the registered /// reference by passing an *empty* \c rosa::Optional object as actual /// argument. If \p Slave is already registered for another position, the /// other position gets cleared. /// /// \note The role of the referred *slave* is validated by checking its /// *kind*. /// /// \note The type of values produced by the referred *slave* is validated by /// matching its `OutputType` against the corresponding value in /// \c rosa::deluxe::DeluxeAgent::InputTypes. /// /// \note The type of master-input values processed by the referred *slave* is /// validated by matching its `MasterInputType` against the corresponding /// value in \c rosa::deluxe::DeluxeAgent::MasterOutputTypes. /// /// \param Pos position to register \p Slave at /// \param Slave the *slave* to register /// /// \pre \p Pos is a valid index of input, \p Slave is empty or of kind /// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind, /// and \p Slave -- if not empty -- produces values of types matching the /// expected input type at position \p Pos and processes values of types /// matching the produced master-output type at position \p Pos: /// \code /// Pos < NumberOfInputs && /// (!Slave || /// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos] && /// (emptyToken(MasterOutputTypes[Pos]) || /// static_cast(unwrapAgent(*Slave)).MasterInputType /// == MasterOutputTypes[Pos])) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos] && /// (emptyToken(MasterOutputTypes[Pos]) || /// static_cast(unwrapAgent(*Slave)).MasterInputType == /// MasterOutputTypes[Pos]))) /// \endcode void registerSlave(const size_t Pos, const Optional Slave) noexcept; /// Tells the position of a registered *slave*. /// /// \param Slave \c rosa::AgentHandle for the *slave* to check /// /// \return position of \p Slave if it is registered and found, /// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise. size_t positionOfSlave(AgentHandle Slave) const noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// The elements from \p Value are sent one by one in separate messages to the /// *master*. /// /// \tparam Ts types of the elements in \p Value /// \tparam S0 indices for accessing elements of \p Value /// /// \param Value value to send /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Ts match \c /// rosa::deluxe::DeluxeiAgent::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void sendToMaster(const DeluxeTuple &Value, Seq) noexcept; /// Sends a value to a *slave* of \p this object at position \p Pos. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Slaves[Pos] if /// it contains a valid handle. The function does nothing otherwise. /// /// The elements from \p Value are sent one by one in separate messages to the /// *slave*. /// /// \tparam Ts types of the elements in \p Value /// \tparam S0 indices for accessing elements of \p Value /// /// \param Pos the position of the *slave* to send \p Value to /// \param Value value to send /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Pos is a valid *slave* position and \p Ts match /// \c rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code /// Pos < NumberOfMasterOutputs && /// MasterOutputTypes[Pos] == TypeToken::Value /// \endcode template void sendToSlave(const size_t Pos, const DeluxeTuple &Value, Seq) noexcept; /// Generates the next output by processing current input values upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeAgent::FP. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. /// /// \pre Master-input and all input from *slaves* are supposed to be /// completely received upon triggering: \code /// MasterInputNextPos == 0 && /// std::all_of(InputNextPos.begin(), InputNextPos.end(), /// [](const token_size_t &I){return I == 0;}) /// \endcode void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from a *slave*. /// /// The function stores \p Value at position \p Pos in \c /// rosa::deluxe::DeluxeAgent::InputValues at the position associated to \p Id /// in \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding /// flag in \c rosa::deluxe::DeluxeAgent::InputChanged. The function also /// takes care of checking and updating \c /// rosa::deluxe::DeluxeSensor::MasterInputNextPos at the corresponding /// position: increments the value and resets it to `0` when the last element /// is received. /// /// \note Utilized by member functions of group \c DeluxeAgentInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of *slave* /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \param Value the input value to store /// /// \pre The *slave* with \p Id is registered, \p Pos is the expected /// position of input from the *slave*, and the input from it is expected to /// be of type \p T: \code /// SlaveIds.find(Id) != SlaveIds.end() && /// Pos == InputNextPos[SlaveIds.find(Id)->second] && /// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == /// TypeNumberOf::Value /// \endcode template void saveInput(id_t Id, token_size_t Pos, T Value) noexcept; /// Stores a new input value from the *master*. /// /// The function stores \p Value at position \p Pos in \c /// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the /// flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. The function also /// takes care of checking and updating \c /// rosa::deluxe::DeluxeAgent::MasterInputNextPos: increments its value and /// reset to `0` when the last element is received. /// /// \note Utilized by member functions of group \c /// DeluxeAgentMasterInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of the *master* /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \param Value the input value to store /// /// \pre The *master* with \p Id is registered, \p Pos is the expected /// position of master-input, and the input from the *master* at position \p /// Pos is expected to be of type \p T: \code /// Master && masterId() == Id && Pos == MasterInputNextPos && /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value /// \endcode template void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; /// \defgroup DeluxeAgentInputHandlers Input handlers of /// rosa::deluxe::DeluxeAgent /// /// Definition of member functions handling messages from *slaves* with /// different types of input /// /// A *master* generally needs to be prepared to deal with values of any /// built-in type to handle messages from its *slaves*. Each type requires a /// separate message handler, which are implemented by these functions. The /// functions instantiate \c rosa::deluxe::DeluxeAgent::saveInput with the /// proper template argument and pass the content of the message on for /// processing. /// /// \note The member functions in this group are defined by \c /// DASLAVEHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DASLAVEHANDLERDEF(AtomValue) DASLAVEHANDLERDEF(int16_t) DASLAVEHANDLERDEF(int32_t) DASLAVEHANDLERDEF(int64_t) DASLAVEHANDLERDEF(int8_t) DASLAVEHANDLERDEFN(long double, long_double) DASLAVEHANDLERDEFN(std::string, std__string) DASLAVEHANDLERDEF(uint16_t) DASLAVEHANDLERDEF(uint32_t) DASLAVEHANDLERDEF(uint64_t) DASLAVEHANDLERDEF(uint8_t) DASLAVEHANDLERDEF(unit_t) DASLAVEHANDLERDEF(bool) DASLAVEHANDLERDEF(double) DASLAVEHANDLERDEF(float) /// @} /// \defgroup DeluxeAgentMasterInputHandlers Master-input handlers of /// rosa::deluxe::DeluxeAgent /// /// Definition of member functions handling messages from the *master* with /// different types of input /// /// A *slave* generally needs to be prepared to deal with values of any /// built-in type to handle messages from its *master*. Each type requires a /// separate message handler, which are implemented by these functions. The /// functions instantiate \c rosa::deluxe::DeluxeAgent::saveMasterInput with /// the proper template argument and pass the content of the message on for /// processing. /// /// \note The member functions in this group are defined by \c /// DAMASTERHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DAMASTERHANDLERDEF(AtomValue) DAMASTERHANDLERDEF(int16_t) DAMASTERHANDLERDEF(int32_t) DAMASTERHANDLERDEF(int64_t) DAMASTERHANDLERDEF(int8_t) DAMASTERHANDLERDEFN(long double, long_double) DAMASTERHANDLERDEFN(std::string, std__string) DAMASTERHANDLERDEF(uint16_t) DAMASTERHANDLERDEF(uint32_t) DAMASTERHANDLERDEF(uint64_t) DAMASTERHANDLERDEF(uint8_t) DAMASTERHANDLERDEF(unit_t) DAMASTERHANDLERDEF(bool) DAMASTERHANDLERDEF(double) DAMASTERHANDLERDEF(float) /// @} }; /// Anonymous namespace with implementation for \c /// rosa::deluxe::DeluxeAgent::DeluxeAgent, \c /// rosa::deluxe::DeluxeAgent::inputTypesMatch, and \c /// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch, consider it private. namespace { /// Calculates storage offsets for values of \p Ts... stored in a \c /// rosa::TokenizedStorage. /// /// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c /// rosa::deluxe::DeluxeAgent::InputStorageOffsets. /// /// \tparam Ts types whose offsets to calculate /// \tparam S0 indices for referring to positions in \p Ts... /// /// \note Instantiation fails if any of the type arguments \p Ts... is not an /// instance of \c rosa::deluxe::DeluxeTuple. /// /// \note The only argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \return \c std::vector containing the calculated offsets /// /// \pre Statically, all the type arguments \p Ts... are instances of \c /// rosa::deluxe::DeluxeTuple and the indices match the types: \code /// TypeListAllDeluxeTuple>::Value && /// sizeof...(Ts) == sizeof...(S0) /// \endcode template < typename... Ts, size_t... S0, typename = std::enable_if_t>::Value>> static std::vector storageOffsets(Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); std::vector Offsets(sizeof...(Ts)); // Do nothing for no types. if constexpr (sizeof...(Ts) != 0) { Offsets[0] = 0; // The offset of the very first value is always `0`. // Calculate further offsets... (((S0 != sizeof...(Ts) - 1) && (Offsets[S0 + 1] = Offsets[S0] + Ts::Length)), ...); } return Offsets; } /// Template \c struct whose specializations provide a recursive implementation /// for \c TypesMatchList. /// /// \tparam As types to match template struct TypesMatchImpl; /// Template specialization for the case, when at least one type is to /// be matched and that is an instance of \c rosa::deluxe::DeluxeTuple. /// /// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple to match /// \tparam As further types to match template struct TypesMatchImpl, As...> { /// Tells whether types \c rosa::deluxe::DeluxeTuple and \p As... match /// \c rosa::Token values stored in \p Tokens starting at position \p Pos. /// /// The function has got a recursive implementation: it matches the first /// type \c rosa::deluxe::DeluxeTuple against \c rosa::Token at /// position \p Pos of \p Tokens, then further types \p As... are matched /// recursively starting at position \c (Pos + 1). /// /// \param Tokens container of \c rosa::Token values to match types against /// \param Pos position in \p Tokens to start matching at /// /// \return if types \c rosa::deluxe::DeluxeTuple and \p As... match \c /// rosa::Token values stored in \p Tokens starting at position \p Pos static bool f(const std::vector &Tokens, size_t Pos) noexcept { return Pos < Tokens.size() && TypeToken::Value == Tokens[Pos] && TypesMatchImpl::f(Tokens, Pos + 1); } }; /// Template specialization for the case, when at least one type is to /// be matched and that is *not* an instance of \c rosa::deluxe::DeluxeTuple. /// /// \tparam T first type to match /// \tparam As further types to match template struct TypesMatchImpl { /// Tells whether types \p T and \p As... match \c rosa::Token values stored /// in \p Tokens starting at position \p Pos. /// /// This specialization is used only when \p T is not an instance of \c /// rosa::deluxe::DeluxeTuple, in which case the match is not successful. /// /// \note The function takes two parameters to match the general signature but /// the actual values are ignored. /// /// \return `false` static bool f(const std::vector &, size_t) noexcept { return false; } }; /// Template specialization for the terminal case, when no type remains to /// check. template <> struct TypesMatchImpl<> { /// Tells whether \p Pos is the number of values stored in \p Tokens. /// /// In this terminal case, there is no more types to match because all the /// types are supposed to be already matched successfully. The whole list of /// types already matched is a complete match if it covers all values in /// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens. /// /// \param Tokens container of \c rosa::Token values to match types against /// \param Pos position in \p Tokens to start matching at /// /// \return if \p Pos is the number of values stored in \p Tokens static bool f(const std::vector &Tokens, size_t Pos) noexcept { return Pos == Tokens.size(); } }; /// Template \c struct that provides an implementation for \c /// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c /// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch. /// /// \note Match a list of types \p List against a \c std::vector of /// \c rosa::Token values, \c Tokens, like \code /// bool match = TypesMatchList::f(Tokens); /// \endcode /// If any type in \c rosa::TypeList \p Listis not an instance of \c /// rosa::deluxe::DeluxeTuple, the match gives a negative result. /// /// \tparam List \c rosa::TypeList that contains types to match template struct TypesMatchList; /// Template specialization implementing the feature. /// /// \tparam As types to match template struct TypesMatchList> { /// Tells whether types \p As... match \c rosa::Token values stored in \p /// Tokens. /// /// The function unwraps the types from \c rosa::TypeList and utilizes \c /// TypesMatchImpl to do the check. /// /// \param Tokens container of \c rosa::Token values to match types against /// /// \return if types \p As... match \c rosa::Token values stored in \p Tokens static bool f(const std::vector &Tokens) noexcept { return TypesMatchImpl::f(Tokens, 0); } }; } // End namespace template bool DeluxeAgent::inputTypesMatch(void) const noexcept { return TypesMatchList::f(InputTypes); } template bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept { return TypesMatchList::f(MasterOutputTypes); } template DeluxeTuple DeluxeAgent::prepareInputValueAtPos(TypeList, Seq) const noexcept { using T = DeluxeTuple; STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); ASSERT(inv() && Pos < NumberOfInputs && T::TT == InputTypes[Pos]); const token_size_t StorageOffset = InputStorageOffsets[Pos]; // The below should hold because of the above, just leave it for sanity check. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); // Get all elements of the tuple in a fold expression. return T(*static_cast(InputValues->pointerTo( static_cast(StorageOffset + S0)))...); } template std::tuple...> DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { STATIC_ASSERT(TypeListAllDeluxeTuple>::Value, "not tuple types"); STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments"); ASSERT(inv() && inputTypesMatch>()); return std::make_tuple(std::make_pair( prepareInputValueAtPos(typename UnwrapDeluxeTuple::Type(), seq_t()), InputChanged[S0])...); } template std::tuple, Optional...> DeluxeAgent::invokeWithTuple( std::function< std::tuple, Optional...>(std::pair...)> F, const std::tuple...> Args, Seq) noexcept { ASSERT(sizeof...(As) == sizeof...(S0)); return F(std::get(Args)...); } template void DeluxeAgent::handleMasterOutputAtPos( const Optional> &Value) noexcept { using MOT = DeluxeTuple; ASSERT(inv() && Pos < NumberOfMasterOutputs && MOT::TT == MasterOutputTypes[Pos]); // Do not do anything for master-output of type \c // rosa::deluxe::EmptyDeluxeTuple and when \p Value is empty. if constexpr (!std::is_same_v) { if (Value) { sendToSlave(Pos, *Value, seq_t()); } } else { (void)Value; } ASSERT(inv()); } template void DeluxeAgent::handleMasterOutputs(const std::tuple...> &Output, Seq) noexcept { using MOTs = typename TypeListDrop>::Type; STATIC_ASSERT(TypeListAllDeluxeTuple::Value, "not tuple type arguments"); STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0), "inconsistent arguments"); ASSERT(inv() && masterOutputTypesMatch() && sizeof...(S0) == NumberOfMasterOutputs); // Handle each master-output position in a fold expression. (handleMasterOutputAtPos(std::get(Output)), ...); ASSERT(inv()); } template DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions( std::function< std::tuple...>(std::pair, bool>)> &&MF, std::function< std::tuple, Optional...>(std::pair...)> &&F, Seq) noexcept { using MT = DeluxeTuple; STATIC_ASSERT((TypeListAllDeluxeTuple>::Value), "not tuple type arguments"); STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments"); ASSERT(MasterInputType == MT::TT && OutputType == T::TT && inputTypesMatch>() && masterOutputTypesMatch>()); return [ this, MF, F ]() noexcept { // \note These indices work for both inputs and master-outputs. using SlaveIndices = seq_t; // Handle master-input. // Do not do anything for master-input type \c // rosa::deluxe::EmptyDeluxeTuple. if (!std::is_same_v) { LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles master-input." << std::endl; // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); const auto MasterInputArg = std::make_pair( // Get all elements of the tuple in a fold expression. MT(*static_cast( MasterInputValue->pointerTo(static_cast(S0)))...), MasterInputChanged); MasterInputChanged = false; const std::tuple...> MasterOutput = MF(MasterInputArg); handleMasterOutputs<0>(MasterOutput, SlaveIndices()); } // Handle inputs. - LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input." - << std::endl; - const auto InputArgs = prepareCurrentInputs(SlaveIndices()); - std::fill(InputChanged.begin(), InputChanged.end(), false); - const std::tuple, Optional...> Output = - invokeWithTuple(F, InputArgs, SlaveIndices()); - const auto OutputToMaster = std::get<0>(Output); - if (OutputToMaster) { - sendToMaster(*OutputToMaster, seq_t()); + // Call the processing function only if \p ExecutionPolicy allows. + if (ExecutionPolicy->shouldProcess(InputChanged)) { + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input." + << std::endl; + const auto InputArgs = prepareCurrentInputs(SlaveIndices()); + std::fill(InputChanged.begin(), InputChanged.end(), false); + const std::tuple, Optional...> Output = + invokeWithTuple(F, InputArgs, SlaveIndices()); + const auto OutputToMaster = std::get<0>(Output); + if (OutputToMaster) { + sendToMaster(*OutputToMaster, seq_t()); + } + handleMasterOutputs<1>(Output, SlaveIndices()); + } else { + LOG_TRACE_STREAM << "DeluxeAgent " << Name << " skips input." + << std::endl; } - handleMasterOutputs<1>(Output, SlaveIndices()); }; } template DeluxeAgent::DeluxeAgent( const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DASLAVEHANDLERREF(AtomValue), DASLAVEHANDLERREF(int16_t), DASLAVEHANDLERREF(int32_t), DASLAVEHANDLERREF(int64_t), DASLAVEHANDLERREF(int8_t), DASLAVEHANDLERREF(long_double), DASLAVEHANDLERREF(std__string), DASLAVEHANDLERREF(uint16_t), DASLAVEHANDLERREF(uint32_t), DASLAVEHANDLERREF(uint64_t), DASLAVEHANDLERREF(uint8_t), DASLAVEHANDLERREF(unit_t), DASLAVEHANDLERREF(bool), DASLAVEHANDLERREF(double), DASLAVEHANDLERREF(float), DAMASTERHANDLERREF(AtomValue), DAMASTERHANDLERREF(int16_t), DAMASTERHANDLERREF(int32_t), DAMASTERHANDLERREF(int64_t), DAMASTERHANDLERREF(int8_t), DAMASTERHANDLERREF(long_double), DAMASTERHANDLERREF(std__string), DAMASTERHANDLERREF(uint16_t), DAMASTERHANDLERREF(uint32_t), DAMASTERHANDLERREF(uint64_t), DAMASTERHANDLERREF(uint8_t), DAMASTERHANDLERREF(unit_t), DAMASTERHANDLERREF(bool), DAMASTERHANDLERREF(double), DAMASTERHANDLERREF(float)), - OutputType(T::TT), NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT), + ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), + NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT), NumberOfMasterOutputs(NumberOfInputs), InputTypes({As::TT...}), InputNextPos(NumberOfInputs, 0), InputChanged(NumberOfInputs, false), InputStorageOffsets(storageOffsets(seq_t())), InputValues(new typename TokenizedStorageForTypeList< typename TypeListUnwrapDeluxeTuple>::Type>:: Type()), MasterInputNextPos(0), MasterInputChanged(false), MasterInputValue(new typename TokenizedStorageForTypeList< typename UnwrapDeluxeTuple::Type>::Type()), MasterOutputTypes({Ts::TT...}), FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F), seq_t())), Slaves(NumberOfInputs) { ASSERT(Kind == atoms::AgentKind); LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl; ASSERT(inv()); } template void DeluxeAgent::sendToMaster(const DeluxeTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(inv() && OutputType == TypeToken::Value); // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); // Create a static constant array for these indices to be available as lvalue // references when creating messages below. \c S0... when used directly in a // fold expression is a temporary value, which would result in \c // rosa::Message instances being created with rvalue references. Further, all // other values would to copied into a temporary variable for making them /// available as rvalue references (they are constant lvalue references here). static constexpr std::array Indices{{S0...}}; LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id << ") sends to master (" << static_cast(Master && *Master) << "): " << Value << " (" << sizeof...(S0) << ")" << std::endl; // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { // Handle each element of the tuple in a fold expression. (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], std::get(Value))), ...); } + ASSERT(inv()); } template void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(inv() && Pos < NumberOfMasterOutputs && MasterOutputTypes[Pos] == TypeToken::Value); // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); // Create a static constant array for these indices to be available as lvalue // references when creating messages below. \c S0... when used directly in a // fold expression is a temporary value, which would result in \c // rosa::Message instances being created with rvalue references. Further, all // other values would to copied into a temporary variable for making them /// available as rvalue references (they are constant lvalue references here). static constexpr std::array Indices{{S0...}}; // There is a handle and the referred *slave* is in a valid state. auto Slave = Slaves[Pos]; LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id << ") sends to slave (" << static_cast(Slave && *Slave) << ") at position " << Pos << ": " << Value << " (" << sizeof...(S0) << ")" << std::endl; if (Slave && *Slave) { // Handle each element of the tuple in a fold expression. (Slave->sendMessage(Message::create(atoms::Master::Value, Id, Indices[S0], std::get(Value))), ...); } } template void DeluxeAgent::saveInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && Pos == InputNextPos[SlaveIds.find(Id)->second] && typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == TypeNumberOf::Value); size_t SlavePos = SlaveIds.at(Id); LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id << ") saves value from slave at position " << SlavePos << ": (" << static_cast(Pos) << ") " << Value << std::endl; // Save value. size_t StoragePos = (size_t)InputStorageOffsets[SlavePos] + Pos; // This assert must hold if \p this object was successfully constructed. ASSERT(static_cast(static_cast(StoragePos)) == StoragePos); *static_cast( InputValues->pointerTo(static_cast(StoragePos))) = Value; // Update position of next value. if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) { InputNextPos[SlavePos] = 0; } // Set flag. InputChanged[SlavePos] = true; ASSERT(inv()); } template void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos && typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id << ") saves value from master: (" << static_cast(Pos) << ") " << Value << std::endl; // Save value. *static_cast(MasterInputValue->pointerTo(Pos)) = Value; // Update position of next value. if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { MasterInputNextPos = 0; } // Set flag. MasterInputChanged = true; ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa #undef DASLAVEHANDLEREF #undef DAMASTERHANDLEREF #undef DASLAVEHANDLEDEF #undef DAMASTERHANDLEDEF #undef DASLAVEHANDLEDEFN #undef DAMASTERHANDLEDEFN #undef DASLAVEHANDLENAME #undef DAMASTERHANDLENAME #endif // ROSA_DELUXE_DELUXEAGENT_HPP diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp old mode 100755 new mode 100644 index 74da611..259b34c --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,887 +1,929 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. /// /// \todo The classes \c rosa::deluxe::DeluxeSensor and \c /// rosa::deluxe::DeluxeAgent share some common features in relation to their /// *slave* role in the *deluxe interface*. But their definitions are completely /// independent. It could be investigated how to lift their common parts into a /// new *deluxe slave* class, which would serve as base for both, to avoid code /// duplication. class DeluxeContext { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, + NotUnit, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, - AlreadyHasValueStream + AlreadyHasValueStream, + UnsuitableExecutionPolicy }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; private: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// /// \note Instantiation fails if any of the type arguments \p MT and \p T /// is not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c /// rosa::deluxe::EmptyDeluxeTuple. /// /// \param Name name of the new *sensor* /// \param MF function for the new *sensors* to process master-input /// values with /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to /// register an alternative simulation data source with \c /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template >::Value && !std::is_same_v>> AgentHandle createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept; public: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* does not receive master-input. /// /// \tparam T type of data the new *sensor* operates on /// /// \note Instantiation fails if type argument \p T is neither a built-in type /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template ::Value || (IsDeluxeTuple::Value && !std::is_same_v)>> AgentHandle createSensor( const std::string &Name, std::function &&F = [](void) { return T(); }) noexcept; /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// /// \note The type arguments \p MT and \p T must be either all built-in types /// or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, \p T cannot be /// \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions /// do not hold. /// /// \param Name name of the new *sensor* /// \param MF function for the new *sensors* to process master-input /// values with \param F function for the new *sensor* to generate /// the next value with during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to /// register an alternative simulation data source with \c /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template , BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same_v)>> AgentHandle createSensor( const std::string &Name, std::function)> &&MF, std::function &&F = [](void) { return T(); }) noexcept; private: /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF and produces /// master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with \param F function for the new *agent* to process /// input values and generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template >::Value && !std::is_same_v && (true && ... && (!std::is_same_v))>> AgentHandle createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; public: /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* neither receives master-input nor produces /// master-output. /// /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p T and \p As... must be either all built-in /// types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, none of /// them can be \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if /// these conditions do not hold. /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template , BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same_v && (true && ... && (!std::is_same_v)))>> AgentHandle createAgent(const std::string &Name, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF but does not /// produce master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p MT, \p T, and \p As... must be either all /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. /// Instantiation fails if these conditions do not hold. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template , BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same_v && (true && ... && (!std::is_same_v)))>> AgentHandle createAgent(const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* does not receive master-input but produces /// master-output. /// /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p T, \p Ts, and \p As... must be either all /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. /// Instantiation fails if these conditions do not hold. /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \note \p F does not produce master-output for a given position if the /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not /// possible to disable master-output at any position by using built-in types. /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template < typename T, typename... Ts, typename... As, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same_v && (true && ... && (!std::is_same_v)))>> AgentHandle createAgent(const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF and produces /// master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either /// all built-in types or all instances of \c rosa::deluxe::DeluxeTuple. /// Moreover, none of \p T and \p As... can be \c /// rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions /// do not hold. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// /// \note \p F does not produce master-output for a given position if the /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not /// possible to disable master-output at any position by using built-in types. /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template , BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same_v && (true && ... && (!std::is_same_v)))>> AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; + /// Returns the current execution policy of the referred \p Unit + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The referred \p Unit is either *sensor* or *agent*. + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeContext::setExecutionPolicy() is not called with the + /// *unit* referred by \p Unit and the *unit* is not destroyed. + /// + /// \param Unit the *unit* whose execution policy is to be obtained + /// + /// \return the \c rosa::deluxe::DeluxeExecutionPolicy from \p Unit if \p Unit + /// is valid + Optional + getExecutionPolicy(AgentHandle Unit) const noexcept; + + /// Sets the current execution policy of the referred \p Unit to \p + /// ExecutionPolicy. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The referred \p Unit is either *sensor* or *agent*. + /// + /// \param Unit the *unit* whose execution policy is to be set + /// \param ExecutionPolicy the new execution policy for \p Unit + /// + /// \return how successful setting \p ExecutionPolicy for \p Unit was + /// + /// \note The function may return the following + /// \c rosa::deluxe::DeluxeContext::ErrorCode values: + /// `ErrorCode` | Comment + /// ----------- | ------- + /// `NoError` | Success + /// `NotUnit` | Referred \p Unit is not valid + /// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit + ErrorCode setExecutionPolicy( + AgentHandle Unit, + std::unique_ptr &&ExecutionPolicy) noexcept; + /// Connects a *sensor* to an *agent* in the context of \p this object. /// /// \param Agent the *agent* to connect to /// \param Pos the index of slot of \p Agent to connect \p Sensor to /// \param Sensor the *sensor* to connect /// \param Description optional textual description of the connection /// /// \return how successfull connecting \p Sensor to \p Agent at slot /// index \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `WrongPosition` | \p Pos is not a valid input position of \p Agent /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other thanthe output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any /// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Sensor already has a *master* registered ErrorCode connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description = "") noexcept; /// Connectes two *agents* in the context of \p this object. /// /// \param Master the *agent* to connect to /// \param Pos the index of slot of \p Master to connect \p Slave to /// \param Slave the *agent* to connect /// \param Description optional textual description of the connection /// /// \return how succesfull connecting \p Slave to \p Master at slot /// index \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent /// `WrongPosition` | \p Pos is not a valid input position of \p Master /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave or expected master-input of \p Slave is other than master-output at position \p Pos of \p Master if any /// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Slave already has a *master* registered ErrorCode connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description = "") noexcept; /// Initializes \c this object and others managed by \p this object /// for setting up and performing simulation. /// /// \see \c rosa::deluxe::DeluxeContext::registerSensorValues, /// \c rosa::deluxe::DeluxeContext::simulate /// /// Need to clear simulation data sources from all the *sensors*. void initializeSimulation(void) noexcept; public: /// Registers a stream providing values for a *sensor* during /// simulation. /// /// \tparam Iterator type of iterator providing values for \p Sensor /// \tparam T type of values \p Sensor is operating on, always use /// default! /// /// \note Instantiation fails if type argument \p T is neither a built-in type /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. /// /// \param Sensor the *sensor* to register values for /// \param Start provides values for \p Sensor /// \param End denotes the end of stream of values /// \param Default value to be used when input stream is depleted /// during simulation /// /// \return how successful registering \p Source for \p Sensor /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `TypeMismatch` | \p Sensor generates values of a type other than /// \p T `NotSensor` | Referred \p Sensor is not \c /// rosa::deluxe::DeluxeSensor `AlreadyHasValueStream` | \p Sensor already has /// simulation data source set template < typename Iterator, typename T = typename Iterator::value_type, typename = std::enable_if_t::Value || (IsDeluxeTuple::Value && !std::is_same_v)>> ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default = {}) noexcept; /// Performs the system contained by \p this object. /// /// The function performs \p NumCycles cycle of simulation. In each /// cycle, all the *agents* and *sensors* registered in \c /// rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for /// execution. /// /// \param NumCycles number of cycles to perform /// /// \pre All the *sensors* in the system contained by \p this object /// generate their output from simulation data sources. void simulate(const size_t NumCycles) const noexcept; }; /// Anonymous namespace with helper features for implementing /// \c rosa::deluxe::DeluxeContext, consider it private. namespace { /// Maps any type \p T to \c rosa::deluxe::EmptyDeluxeTuple. template struct MapToEmptyDeluxeTuple { using Type = EmptyDeluxeTuple; }; /// Convenience template alias for \c MapToEmptyDeluxeTuple. template using empty_deluxe_t = typename MapToEmptyDeluxeTuple::Type; /// Converts a \c std::tuple of \c rosa::Optional built-in types into a /// corresponding \c std::tuple of \c rosa::Optional with each actual value /// wrapped in \c rosa::deluxe::DeluxeTuple. /// /// \tparam Ts types of the values /// \tparam S0 indices for accessing values in \p Values /// /// \param Values the \c std::tuple of \c rosa::Optional with built-in values /// /// \note The second argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values /// with each actual value wrapped in \c rosa::deluxe::DeluxeTuple /// /// \pre Statically, all type arguments \p Ts... are built-in types and the /// provided indices \p S0... match the length of \p Ts...: \code /// TypeListSubsetOf, BuiltinTypes>::Value && /// sizeof...(Ts) == sizeof...(S0) /// \endcode template std::tuple>...> wrapBuiltinInDeluxeTuple(const std::tuple...> &Values, Seq) noexcept { STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), "not built-in types"); STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); return std::make_tuple(std::get(Values) ? Optional>( make_deluxe_tuple(*std::get(Values))) : Optional>()...); } } // End namespace template AgentHandle DeluxeContext::createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } template AgentHandle DeluxeContext::createSensor(const std::string &Name, std::function &&F) noexcept { auto EmptyMF = std::function)>( [](std::pair) {}); if constexpr (TypeListContains::Value) { using OutputType = DeluxeTuple; return createSensorImpl( Name, std::move(EmptyMF), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (IsDeluxeTuple::Value && !std::is_same_v) { return createSensorImpl(Name, std::move(EmptyMF), std::move(F)); } else { ASSERT(false && "Unexpected type argument"); } } template AgentHandle DeluxeContext::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using OutputType = DeluxeTuple; return createSensorImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); }), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same_v) { return createSensorImpl(Name, std::move(MF), std::move(F)); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; auto EmptyMF = std::function)>( [](std::pair) { return NoMasterOutputType(); }); if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using OutputType = DeluxeTuple; return createAgentImpl( Name, std::move(EmptyMF), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return std::tuple_cat( wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), NoMasterOutputType()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same_v && (true && ... && (!std::is_same_v))) { return createAgentImpl( Name, std::move(EmptyMF), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); return NoMasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return std::tuple_cat( wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), NoMasterOutputType()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same_v && (true && ... && (!std::is_same_v))) { return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF(Arg); return NoMasterOutputType(); }), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterOutputType = std::tuple>...>; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return wrapBuiltinInDeluxeTuple(Result, seq_t<1 + sizeof...(Ts)>()); })); } else if constexpr (TypeListAllDeluxeTuple< TypeList>::Value && !std::is_same_v && (true && ... && (!std::is_same_v))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using MasterOutputType = std::tuple>...>; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Result = MF({std::get<0>(Arg.first), Arg.second}); return wrapBuiltinInDeluxeTuple(Result, seq_t()); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return wrapBuiltinInDeluxeTuple(Result, seq_t<1 + sizeof...(Ts)>()); })); } else if constexpr (TypeListAllDeluxeTuple< TypeList>::Value && !std::is_same_v && (true && ... && (!std::is_same_v))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Output = MF(Arg); return Output; }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template DeluxeContext::ErrorCode DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default) noexcept { // Get the type of values provided by \p Iterator. STATIC_ASSERT((std::is_same::value), "type mismatch"); // Make sure preconditions are met. if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } if constexpr (TypeListContains::Value) { if (S->OutputType != TypeToken::Value) { DCRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource(std::function(void)>([= ](void) mutable noexcept->DeluxeTuple { if (Start != End) { LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName << "': " << *Start << '\n'; return make_deluxe_tuple(*Start++); } else { LOG_TRACE_STREAM << "Providing default value for sensor '" << S->FullName << "': " << Default << '\n'; return make_deluxe_tuple(Default); } })); } else if constexpr (IsDeluxeTuple::Value && !std::is_same_v) { if (S->OutputType != T::TT) { DCRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource( std::function([=](void) mutable noexcept->T { if (Start != End) { LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName << "': " << *Start << '\n'; return *Start++; } else { LOG_TRACE_STREAM << "Providing default value for sensor '" << S->FullName << "': " << Default << '\n'; return Default; } })); } else { ASSERT(false && "Unexpected type argument"); } return ErrorCode::NoError; } } // End namespace deluxe } // End namespace rosa // Undef local macro if not used in the corresponding implementation. #ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP #undef DCRETERROR #endif #endif // ROSA_DELUXE_DELUXECONTEXT_HPP diff --git a/include/rosa/deluxe/DeluxeExecutionPolicy.h b/include/rosa/deluxe/DeluxeExecutionPolicy.h new file mode 100644 index 0000000..caaad92 --- /dev/null +++ b/include/rosa/deluxe/DeluxeExecutionPolicy.h @@ -0,0 +1,195 @@ +//===-- rosa/deluxe/DeluxeExecutionPolicy.h ---------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/deluxe/DeluxeExecutionPolicy.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Public interface of *execution policies* in the *deluxe interface*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H +#define ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H + +#include "rosa/core/AgentHandle.hpp" + +#include +#include +#include +#include + +namespace rosa { +namespace deluxe { + +// Forward declaration of DeluxeSystem. Do not include the corresponding header +// in this file because of cyclic dependency. +class DeluxeSystem; + +/// *Execution policy* that controls how *agents* and *sensors* call their +/// processing functions. +/// +/// An *execution policy* can be applied to a deluxe *unit* only if \c +/// deluxe::rosa::DeluxeExecutionPolicy::canHandle() allows it. Each deluxe +/// *unit* must have a compatible *execution policy* associated to it, and the +/// *unit* queries \c rosa::deluxe::DeluxeExecutionPolicy::shouldProcess() on each +/// triggering and calls its processing funtion only if it is allowed by the +/// *execution policy*. +/// +/// \see rosa::deluxe::DeluxeExecutionPolicy::decimation() +/// \see rosa::deluxe::DeluxeExecutionPolicy::awaitAll() +/// \see rosa::deluxe::DeluxeExecutionPolicy::awaitAny() +/// +/// \todo Extend the interface with query functions about what kind of +/// execution policy is behind the interface. This can be done in relation +/// to the existing factory functions; for example, if the actual object is +/// decimation and with what rate. +class DeluxeExecutionPolicy { +protected: + + /// Protected constructor, only implementations can instantiate the class. + DeluxeExecutionPolicy(void) noexcept = default; + +private: + /// No instance can be copy-constructed, move-constructed, copied, and moved. + /// + ///@{ + DeluxeExecutionPolicy(const DeluxeExecutionPolicy &) = delete; + DeluxeExecutionPolicy(DeluxeExecutionPolicy &&) = delete; + DeluxeExecutionPolicy &operator=(const DeluxeExecutionPolicy &) = delete; + DeluxeExecutionPolicy &operator=(DeluxeExecutionPolicy &&) = delete; + ///@} + +public: + /// Virtual destructor for subclasses. + virtual ~DeluxeExecutionPolicy(void) noexcept = default; + + /// Creates an *execution policy* that allows execution with decimation of + /// triggering. + /// + //// *Decimation* can handle both *agents* and *sensors*. + /// Processing functions are executed only on every \p D th + /// triggering. In the case of *sensors* in simulation, the simulation data + /// source is read on each triggering as it provides values with respect to + /// the highest execution frequency, but output is generated by the *sensor* + /// only on every \p D th triggering. + /// + /// \note A rate of \c 0 is allowed as actual argument and is treated as rate + /// \c 1 (i.e., execute processing functions on each triggering). + /// + /// \param D the rate of *decimation* + /// + /// \return an *execution policy* implementing *decimation* with rate \p D + static std::unique_ptr decimation(const size_t D); + + /// Creates an *execution policy* that allows execution only if all defined + /// *slave* positions has new input. + /// + /// *Await all* can handle only *agents* and only if the particular *agent* + /// has at least as many *slave* positions as the largest position defined in + /// \p S. Processing functions are executed only if new input has been + /// received for all defined *slave* positions. + /// + /// \param S set of *slave* positions to await input from + /// + /// \return an *execution policy* implementing *awaiting all* input from set + /// \p S + static std::unique_ptr + awaitAll(const std::set &S); + + /// Creates an *execution policy* that allows execution if any of the defined + /// *slave* positions has new input. + /// + /// *Await any* can handle only *agents* and only if the particular *agent* + /// has at least as many *slave* positions as the largest position defined in + /// \p S. Processing functions are executed if new input has been received for + /// any of the defined *slave* positions. + /// + /// \param S set of *slave* positions to await input from + /// + /// \return an *execution policy* implementing *awaiting any* input from set + /// \p S + static std::unique_ptr + awaitAny(const std::set &S); + + /// Tells if \p this object can handle the deluxe *unit* referred by \p H. + /// + /// The *execution policy* implemented by \p this object is applicable to the + /// given deluxe *unit* referred by \p H only if the function returns \c true. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if \p this object can handle the *unit* referred by \p H + virtual bool canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept = 0; + + /// Tells if processing function should be executed on the current triggering. + /// + /// The function is to be called on each triggering of the deluxe *unit*. + /// Decision about execution of processing function is done by \p this object + /// according to the implemented *execution policy*. + /// + /// \param InputChanged flags indicating whether new input has been received + /// at *slave* positions + /// + /// \return if to execute processing function + virtual bool shouldProcess(const std::vector &InputChanged) noexcept = 0; + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + virtual std::string dump(void) const noexcept = 0; + +protected: + /// Tells whether the *unit* referred by \p H is a \c + /// rosa::deluxe::DeluxeAgent. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if the *unit* referred by \p H is a \c rosa::deluxe::DeluxeAgent + bool isDeluxeAgent(const AgentHandle H, const DeluxeSystem &S) const noexcept; + + /// Tells the number of inputs handled by the *unit* referred by \p H. + /// + /// If \p H refers to a \c rosa::deluxe::DeluxeAgent, the function returns the + /// number of inputs (i.e., *slave* positions) of the *agent*. Otherwise, the + /// function returns \c 0. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return the number of inputs handled by the *unit* referred by \p H + size_t numberOfDeluxeAgentInputs(const AgentHandle H, + const DeluxeSystem &S) const noexcept; +}; + +} // End namespace deluxe +} // End namespace rosa + +namespace std { + +/// Converts a \c rosa::deluxe::DeluxeExecutionPolicy into \c std::string. +/// +/// \param EP \c rosa::deluxe::DeluxeExecutionPolicy to convert +/// +/// \return \c std::string representing \p EP +string to_string(const rosa::deluxe::DeluxeExecutionPolicy &EP); + +/// Dumps a \c rosa::deluxe::DeluxeExecutionPolicy to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param EP \c rosa::deluxe::DeluxeExecutionPolicy to dump +/// +/// \return \p OS after dumping \p EP to it +ostream &operator<<(ostream &OS, const rosa::deluxe::DeluxeExecutionPolicy &EP); + +} // End namespace std + +#endif // ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp old mode 100755 new mode 100644 index 40c0ffb..71d6a0f --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,600 +1,659 @@ //===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESENSOR_HPP #define ROSA_DELUXE_DELUXESENSOR_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" +#include "rosa/deluxe/DeluxeExecutionPolicy.h" #include "rosa/deluxe/DeluxeTuple.hpp" /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeSensor. /// /// \param N name suffix to use #define DSMASTERHANDLERNAME(N) handleMaster_##N /// Defines member functions for handling messages from *master* in /// \c rosa::deluxe::DeluxeSensor. /// /// \see \c DeluxeSensorMasterInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeSensor::saveMasterInput to do /// that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DSMASTERHANDLERDEFN(T, N) \ void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \ T Value) noexcept { \ saveMasterInput(MasterId, Pos, Value); \ } /// Convenience macro for \c DSMASTERHANDLERDEFN with identical arguments. /// /// \see \c DSMASTERHANDLERDEFN /// /// This macro can be used instead of \c DSMASTERHANDLERDEFN if the actual value /// of \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DSMASTERHANDLERDEF(T) DSMASTERHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DSMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeSensor to initialize super /// class \c rosa::Agent with member function defined by \c DSMASTERHANDLERDEFN. /// /// \see \c DSMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DSMASTERHANDLERREF(N) THISMEMBER(DSMASTERHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *sensor* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// -/// \todo Add \c rosa::deluxe::DeluxeSensor::MasterInputNextPos to the class -/// invariant once having one. +/// \invariant There is a compatible *execution policy* set; the actual value in +/// \c rosa::deluxe::DeluxeSensor::MasterInputNextPos is valid with respect to +/// the corresponding types. +/// +/// \see Definition of \c rosa::deluxe::DeluxeSensor::inv on the class invariant +/// +/// \note All member functions validate the class invariant as part of their +/// precondition. Moreover, non-const functions validate the invariant before +/// return as their postcondition. class DeluxeSensor : public Agent { + /// Checks whether \p this object holds the class invariant. + /// + /// \see Invariant of the class \c rosa::deluxe::DeluxeSensor + /// + /// \return if \p this object holds the class invariant + bool inv(void) const noexcept; + + /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of + /// \c this object. + std::unique_ptr ExecutionPolicy; + public: /// The type of values produced by \p this object. /// /// That is the types of values \p this object sends to its *master* in a /// \c rosa::deluxe::DeluxeTuple. /// /// \see \c rosa::deluxe::DeluxeSensor::master const Token OutputType; /// The type of values \p this object processes from its *master*. /// /// That is the types of values \p this object receives from its *master* in a /// \c rosa::deluxe::DeluxeTuple. /// /// \see \c rosa::deluxe::DeluxeSensor::master const Token MasterInputType; private: /// Indicates which element of the master-input is expected from the *master*. /// /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value /// element by element in their order of definition. This member field tells /// the element at which position should be received next. /// /// \p this object is supposed to be triggered only when a complete /// master-input has been received, that is the field should hold the value /// `0`. /// /// \see \c rosa::deluxe::DeluxeSensor::handleTrigger /// \c rosa::deluxe::DeluxeSensor::saveMasterInput token_size_t MasterInputNextPos; /// Indicates whether the input value from the *master* has been changed since /// the last trigger received from the system. /// /// The flag is reset to \c false upon handling a trigger and then set to \c /// true by \c rosa::deluxe::DeluxeSensor::saveMasterInput when storig a new /// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue. bool MasterInputChanged; /// Stores the actual input value from *master*. /// /// \note The type of the stored value matches the types indicated by \c /// rosa::deluxe::DeluxeSensor::MasterInputType. const std::unique_ptr MasterInputValue; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeSensor. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c DeluxeSensorTriggerHandlers using H = std::function; /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of /// rosa::deluxe::DeluxeSensor /// /// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor /// /// The actual data source functions and master-input processing function are /// captured in lambda expressions that are in turn wrapped in \c /// std::function objects. The lambda expression calls a processing function, /// either to handle master-input or obtain the next sensory value from data /// source. The next sensory value is sent it to *master* by calling \c /// rosa::deluxe::DeluxeSensor::sendToMaster. Also, the flag \c /// rosa::deluxe::DeluxeSensor::MasterInputChanged is reset when the current /// value is passed to the master-input processing function. The function \c /// rosa::deluxe::DeluxeSensor::handleTrigger needs only to call the proper /// function object. /// Processes master-input. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is called upon the sensor is trigged by the system. const H MFP; /// Produces the next sensory value during normal execution. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is used during normal execution. During simulation, the /// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is /// used instead of \c rosa::deluxe::DeluxeSensor::FP. const H FP; /// Produces the next sensory value during simulation. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is empty by default. The simulation environment sets it to be /// used during simulation. H SFP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a /// given moment. Optional Master; /// Tells the unique identifier of the *master* of \p this object, if any /// registered. /// /// \return the unique identifier of the *master* /// /// \pre A *master* is registered for \p this object: \code /// Master /// \endcode id_t masterId(void) const noexcept; /// Wraps a master-input processing function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers /// /// \tparam Ts types of elements of master-input processed by \p MF /// \tparam S0 indices for accessing master-input values /// /// \param MF function that processes master-input /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates /// that \p this object does not receive master-input, \p MF is never called /// if \p Ts is empty. /// /// \return trigger handler function based on \p MF /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Ts... match \c /// rosa::deluxe::DeluxeSensor::MasterInputType: \code /// MasterInputType == DeluxeTuple::TT /// \endcode template H triggerHandlerFromProcessingFunction( std::function, bool>)> &&MF, Seq) noexcept; /// Wraps a data source function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeSensor::FP, \c /// rosa::deluxe::DeluxeSensor::SFP, and \c DeluxeSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with + /// \param inSimulation if F is a data source for Simulation /// /// \return trigger handler function based on \p F /// /// \pre Statically, the type agument \p T is an instance of \c /// rosa::deluxe::DeluxeTuple: \code /// IsDeluxeTuple::Value /// \endcode Dynamically, \p T matches \c /// rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == T::TT /// \endcode template - H triggerHandlerFromDataSource(std::function &&F) noexcept; + H triggerHandlerFromDataSource(std::function &&F, + bool inSimulation) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// \todo Enforce \p F and \p MF do not potentially throw exception. /// /// \tparam MT type of master-input handled by \p MF /// \tparam T type of data to operate on /// /// \note Instantiation fails if any of the type arguments \p MT and \p T is /// not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c /// rosa::deluxe::EmptyDeluxeTuple. /// /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed /// object does not receive master-input. /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param MF function to process master-input values with /// \param F function to generate the next value with during normal operation /// /// \pre Statically, \p MT and \p T are instances of \c /// rosa::deluxe::DeluxeTuple and \p T contains at least one element:\code /// TypeListAllDeluxeTuple>::Value && T::Length > 0 /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind: /// \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode /// /// \see \c rosa::deluxe::DeluxeTuple template < typename MT, typename T, typename = std::enable_if_t< TypeListAllDeluxeTuple>::Value && (T::Length > 0)>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) noexcept; + /// Returns the current execution policy of \p this object. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeSensor::setExecutionPolicy() is not called and \p this + /// object is not destroyed. + /// + /// \return \c rosa::deluxe::DeluxeSensor::ExecutionPolicy + const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; + + /// Sets the current execution policy of \p this object to \p EP. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note \p EP is set only if it can handle \p this object. + /// + /// \param EP the new execution policy for \p this object + /// + /// \return if \p EP was successfully set for \p this object. + bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; + /// The *master* of \p this object, if any. /// /// \see \c rosa::deluxe::DeluxeSensor::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \note Any call to \c rosa::deluxe::DeluxeSensor::registerMaster should be /// paired with a corresponding call of \c /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that /// input/output types of master and slave matches. /// /// \param _Master the *master* to register /// /// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr. void clearSimulationDataSource(void) noexcept; /// Tells whether a simulation trigger handler is set for \p this object. /// /// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not /// \c nullptr. /// /// \return if a simulation trigger handler is set for \p this object. bool simulationDataSourceIsSet(void) const noexcept; /// Registers a simulation data source for \p this object. /// /// A new simulation trigger handler wrapping \p SF is stored in /// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered /// simulation data source. /// /// \todo Enforce SF does not potentially throw exception. /// /// \tparam Ts types of elements of values provided by \p SF /// /// \param SF function to generate value with /// /// \pre \p Ts... match \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void registerSimulationDataSource( std::function(void)> &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// The elements from \p Value are sent one by one in separate messages to the /// *master*. /// /// \tparam Ts types of the elements in \p Value /// \tparam S0 indices for accessing elements of \p Value /// /// \param Value value to send /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Ts match \c /// rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void sendToMaster(const DeluxeTuple &Value, Seq) noexcept; /// Handles master-input and generates the next sensory value upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input /// and data generating function \c rosa::deluxe::DeluxeSensor::FP or \c /// rosa::deluxe::DeluxeSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. /// /// \pre Master-input is supposed to be completely received upon triggering: /// \code /// MasterInputNextPos == 0 /// \endcode void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from the *master*. /// /// The function stores \p Value at position \p Pos in \c /// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the /// flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. The function also /// takes care of checking and updating \c /// rosa::deluxe::DeluxeSensor::MasterInputNextPos: increments its value and /// resets it to `0` when the last element is received. /// /// \note Utilized by member functions of group \c /// DeluxeSensorMasterInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of the *master* /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \param Value the input value to store /// /// \pre The *master* with \p Id is registered, \p Pos is the expected /// position of master-input, and the input from the *master* at position \p /// Pos is expected to be of type \p T: \code /// Master && masterId() == Id && Pos == MasterInputNextPos && /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value /// \endcode template void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; /// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of /// rosa::deluxe::DeluxeSensor /// /// Definition of member functions handling messages from the *master* with /// different types of input /// /// A *slave* generally needs to be prepared to deal with values of any /// built-in type to handle messages from its *master*. Each type requires a /// separate message handler, which are implemented by these functions. The /// functions instantiate \c rosa::deluxe::DeluxeSensor::saveMasterInput with /// the proper template argument and pass the content of the message on for /// processing. /// /// \note The member functions in this group are defined by \c /// DSMASTERHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DSMASTERHANDLERDEF(AtomValue) DSMASTERHANDLERDEF(int16_t) DSMASTERHANDLERDEF(int32_t) DSMASTERHANDLERDEF(int64_t) DSMASTERHANDLERDEF(int8_t) DSMASTERHANDLERDEFN(long double, long_double) DSMASTERHANDLERDEFN(std::string, std__string) DSMASTERHANDLERDEF(uint16_t) DSMASTERHANDLERDEF(uint32_t) DSMASTERHANDLERDEF(uint64_t) DSMASTERHANDLERDEF(uint8_t) DSMASTERHANDLERDEF(unit_t) DSMASTERHANDLERDEF(bool) DSMASTERHANDLERDEF(double) DSMASTERHANDLERDEF(float) /// @} }; template DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction( std::function, bool>)> &&MF, Seq) noexcept { using MT = DeluxeTuple; STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(MasterInputType == MT::TT); return [ this, MF ](void) noexcept { // Do not do anything for master-input type \c // rosa::deluxe::EmptyDeluxeTuple. if (!std::is_same_v) { LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " handles master-input." << std::endl; // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); const auto MasterInputArg = std::make_pair( // Get all elements of the tuple in a fold expression. DeluxeTuple(*static_cast( MasterInputValue->pointerTo(static_cast(S0)))...), MasterInputChanged); MasterInputChanged = false; MF(MasterInputArg); } }; } template -DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource( - std::function &&F) noexcept { +DeluxeSensor::H +DeluxeSensor::triggerHandlerFromDataSource(std::function &&F, + bool inSimulation) noexcept { STATIC_ASSERT(IsDeluxeTuple::Value, "not tuple type argument"); ASSERT(OutputType == T::TT); - return [ this, F ](void) noexcept { - LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " obtains next value." - << std::endl; - sendToMaster(F(), seq_t()); + return [ this, F, inSimulation ](void) noexcept { + // Get value and send it to master only if \p ExecutionPolicy allows it. + if (ExecutionPolicy->shouldProcess({})) { + LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value." + << std::endl; + sendToMaster(F(), seq_t()); + } else { + LOG_TRACE_STREAM << "DeluxeSensor " << Name << " skips next value." + << std::endl; + if (inSimulation) { + // But read input value in Simulation anyway as input values are + // provided for the highest execution frequency for simulation + F(); + } + } }; } template DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t), DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t), DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double), DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t), DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t), DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t), DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double), DSMASTERHANDLERREF(float)), - OutputType(T::TT), MasterInputType(MT::TT), MasterInputChanged(false), + ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), + MasterInputType(MT::TT), MasterInputChanged(false), MasterInputValue(new typename TokenizedStorageForTypeList< typename UnwrapDeluxeTuple::Type>::Type()), MFP(triggerHandlerFromProcessingFunction(std::move(MF), seq_t())), - FP(triggerHandlerFromDataSource(std::move(F))), SFP(nullptr) { + FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " is created." << std::endl; + ASSERT(inv()); } template void DeluxeSensor::registerSimulationDataSource( std::function(void)> &&SF) noexcept { ASSERT(OutputType == TypeToken::Value); - SFP = triggerHandlerFromDataSource(std::move(SF)); + SFP = triggerHandlerFromDataSource(std::move(SF), true); + ASSERT(inv()); } template void DeluxeSensor::sendToMaster(const DeluxeTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(OutputType == TypeToken::Value); // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); // Create a static constant array for these indices to be available as lvalue // references when creating messages below. \c S0... when used directly in a // fold expression is a temporary value, which would result in \c // rosa::Message instances being created with rvalue references. Further, all // other values would to copied into a temporary variable for making them /// available as rvalue references (they are constant lvalue references here). static constexpr std::array Indices{{S0...}}; LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id << ") sends to master(" << static_cast(Master && *Master) << "): " << Value << std::endl; // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { // Handle each element of the tuple in a fold expression. (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], std::get(Value))), ...); } + ASSERT(inv()); } template void DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos && typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id << ") saves value from master: (" << static_cast(Pos) << ") " << Value << std::endl; // Save value. *static_cast(MasterInputValue->pointerTo(Pos)) = Value; // Update position of next value. if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { MasterInputNextPos = 0; } // Set flag. MasterInputChanged = true; } } // End namespace deluxe } // End namespace rosa #undef DSMASTERHANDLEREF #undef DSMASTERHANDLEDEF #undef DSMASTERHANDLEDEFN #undef DSMASTERHANDLENAME #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp index 14b9464..f39ce5a 100755 --- a/include/rosa/deluxe/DeluxeSystem.hpp +++ b/include/rosa/deluxe/DeluxeSystem.hpp @@ -1,239 +1,240 @@ //===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::MessagingSystem for the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESYSTEM_HPP #define ROSA_DELUXE_DELUXESYSTEM_HPP #include "rosa/core/MessagingSystem.hpp" #include "rosa/deluxe/DeluxeAgent.hpp" #include "rosa/deluxe/DeluxeSensor.hpp" namespace rosa { namespace deluxe { /// Implements and extends the \c rosa::MessagingSystem interface to be /// used by \c rosa::deluxe::DeluxeContext. /// /// The class is a specialization of \c rosa::MessagingSystem, where objects /// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor /// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the /// \c rosa::MessagingSystem interface with features required to implement the /// *deluxe interface*. /// /// \see rosa::deluxe::DeluxeContext class DeluxeSystem : public MessagingSystem { friend class DeluxeContext; + friend class DeluxeExecutionPolicy; public: /// Returns an object implementing the \c rosa::deluxe::DeluxeSystem /// interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::DeluxeSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. DeluxeSystem(void) noexcept = default; public: /// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object /// and returns a \p rosa::AgentHandle for it. /// /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeSensor /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on /// /// \note Type arguments \p MT and \p T must be instances of \c /// rosa::deluxe::DeluxeTuple. /// /// \param Name name of the new \c rosa::deluxe::DeluxeSensor /// \param MF function to process master-input values /// \param F function to generate the next value with during normal operation /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor template AgentHandle createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept; /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object /// and returns a \c rosa::AgentHandle for it. /// /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeAgent /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs /// \tparam Ts types of master-output the new \c rosa::deluxe::DeluxeAgent /// produces /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes /// /// \note Type arguments \p MT, \p T, \p Ts..., and \p As... must be /// instances of \c rosa::deluxe::DeluxeTuple. /// /// \param Name name of the new \c rosa::deluxe::DeluxeAgent /// \param MF function for the new \c rosa::deluxe::DeluxeAgent to process /// master-input values and generate master-output with /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process /// input values and generate output and master-output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent template AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; protected: /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeSensor owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by /// \p this object virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if /// \p H refers to an object which is of that type and is owned by \p this /// object Optional getDeluxeSensor(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeSensor reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeSensor object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeSensor(AgentHandle &H) const noexcept; /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeAgent owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by /// \p this object virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H /// refers to an object which is of that type and is owned by \p this object Optional getDeluxeAgent(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeAgent reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeAgent object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeAgent(AgentHandle &H) const noexcept; }; template AgentHandle DeluxeSystem::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { Agent &DS = createUnit( [&](const id_t Id, MessagingSystem &S) { return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF), std::move(F)); }); return {DS}; } template AgentHandle DeluxeSystem::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { Agent &DA = createUnit( [&](const id_t Id, DeluxeSystem &S) { return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(MF), std::move(F)); }); return {DA}; } } // End namespace deluxe } // End namespace rosa #endif // ROSA_LIB_DELUXE_DELUXESYSTEM_HPP diff --git a/lib/agent/CMakeLists.txt b/lib/agent/CMakeLists.txt index 81a33f5..7edb556 100644 --- a/lib/agent/CMakeLists.txt +++ b/lib/agent/CMakeLists.txt @@ -1,14 +1,18 @@ set(LIB_INCLUDE_DIR ${ROSA_MAIN_INCLUDE_DIR}/rosa/agent) add_library(ROSAAgent ${LIB_INCLUDE_DIR}/namespace.h namespace.cpp ${LIB_INCLUDE_DIR}/Functionality.h Functionality.cpp ${LIB_INCLUDE_DIR}/Abstraction.hpp Abstraction.cpp + ${LIB_INCLUDE_DIR}/FunctionAbstractions.hpp + FunctionAbstractions.cpp + ${LIB_INCLUDE_DIR}/RangeConfidence.hpp + RangeConfidence.cpp ${LIB_INCLUDE_DIR}/History.hpp History.cpp ${LIB_INCLUDE_DIR}/Confidence.hpp Confidence.cpp ) diff --git a/lib/agent/FunctionAbstractions.cpp b/lib/agent/FunctionAbstractions.cpp new file mode 100644 index 0000000..0206760 --- /dev/null +++ b/lib/agent/FunctionAbstractions.cpp @@ -0,0 +1,20 @@ +//===-- agent/FunctionAbstractions.cpp --------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file agent/FunctionAbstractions.cpp +/// +/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for rosa/agent/FunctionAbstractions.hpp. +/// +/// \note Empty implementation, source file here to have a compile database +/// entry for rosa/agent/FunctionAbstractions.hpp. +/// +//===----------------------------------------------------------------------===// + +#include "rosa/agent/FunctionAbstractions.hpp" diff --git a/lib/agent/RangeConfidence.cpp b/lib/agent/RangeConfidence.cpp new file mode 100644 index 0000000..415f11f --- /dev/null +++ b/lib/agent/RangeConfidence.cpp @@ -0,0 +1,20 @@ +//===-- agent/RangeConfidence.cpp -------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file agent/RangeConfidence.cpp +/// +/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for rosa/agent/RangeConfidence.hpp. +/// +/// \note Empty implementation, source file here to have a compile database +/// entry for rosa/agent/RangeConfidence.hpp. +/// +//===----------------------------------------------------------------------===// + +#include "rosa/agent/RangeConfidence.hpp" diff --git a/lib/deluxe/CMakeLists.txt b/lib/deluxe/CMakeLists.txt old mode 100755 new mode 100644 index 78e0ee8..56b7af1 --- a/lib/deluxe/CMakeLists.txt +++ b/lib/deluxe/CMakeLists.txt @@ -1,22 +1,32 @@ set(LIB_INCLUDE_DIR ${ROSA_MAIN_INCLUDE_DIR}/rosa/deluxe) add_library(ROSADeluxe ${LIB_INCLUDE_DIR}/namespace.h namespace.cpp ${LIB_INCLUDE_DIR}/DeluxeAtoms.hpp DeluxeAtoms.cpp ${LIB_INCLUDE_DIR}/DeluxeTuple.hpp DeluxeTuple.cpp + executionpolicies/Decimation.h + executionpolicies/Decimation.cpp + executionpolicies/AwaitBase.h + executionpolicies/AwaitBase.cpp + executionpolicies/AwaitAll.h + executionpolicies/AwaitAll.cpp + executionpolicies/AwaitAny.h + executionpolicies/AwaitAny.cpp + ${LIB_INCLUDE_DIR}/DeluxeExecutionPolicy.h + DeluxeExecutionPolicy.cpp ${LIB_INCLUDE_DIR}/DeluxeSensor.hpp DeluxeSensor.cpp ${LIB_INCLUDE_DIR}/DeluxeAgent.hpp DeluxeAgent.cpp ${LIB_INCLUDE_DIR}/DeluxeSystem.hpp DeluxeSystem.cpp DeluxeSystemImpl.hpp DeluxeSystemImpl.cpp ${LIB_INCLUDE_DIR}/DeluxeContext.hpp DeluxeContext.cpp ) ROSA_add_library_dependencies(ROSADeluxe ROSACore) diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp old mode 100755 new mode 100644 index 762e9cb..8f6669e --- a/lib/deluxe/DeluxeAgent.cpp +++ b/lib/deluxe/DeluxeAgent.cpp @@ -1,280 +1,313 @@ //===-- deluxe/DeluxeAgent.cpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeAgent.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Implementation of rosa/deluxe/DeluxeAgent.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/deluxe/DeluxeAgent.hpp" -#include "rosa/deluxe/DeluxeSensor.hpp" +#include "rosa/deluxe/DeluxeSystem.hpp" #include namespace rosa { namespace deluxe { bool DeluxeAgent::inv(void) const noexcept { + // Check execution policy. + // \note The \c rosa::System the \c rosa::Unit is created with is a + // \c rosa::DeluxeSystem. + const DeluxeSystem &DS = static_cast(Unit::system()); + if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) { + return false; + } + // Check number of inputs and master-outputs. if (NumberOfInputs != NumberOfMasterOutputs) { return false; } // Check container sizes. if (!(InputTypes.size() == NumberOfInputs && InputNextPos.size() == NumberOfInputs && InputChanged.size() == NumberOfInputs && InputStorageOffsets.size() == NumberOfInputs && InputValues->size() == InputStorageOffsets[NumberOfInputs - 1] + lengthOfToken(InputTypes[NumberOfInputs - 1]) && MasterOutputTypes.size() == NumberOfInputs // == NumberOfMasterOutputs && Slaves.size() == NumberOfInputs)) { return false; } // Stores storage offset for the next slave position checked in the following // loop. token_size_t InputStorageOffset = 0; // Check *slave* types and validate *slave* registrations and reverse lookup // information. std::map RefIds; // Build up a reference of SlaveIds in this. for (size_t I = 0; I < NumberOfInputs; ++I) { // First, validate the corresponding storage offset value. if (InputStorageOffsets[I] != InputStorageOffset) { return false; } // Fetch type-related information for the input position. const Token T = InputTypes[I]; const token_size_t TL = lengthOfToken(T); const size_t StorageOffset = InputStorageOffsets[I]; // Update storage offset for the next position. InputStorageOffset += TL; // Validate input types at position \c I. for (token_size_t TI = 0; TI < TL; ++TI) { const size_t ElemOffset = StorageOffset + TI; // The assert must hold if \p this object was successfuuly constructed. ASSERT(static_cast(static_cast(ElemOffset)) == ElemOffset); if (InputValues->typeAt(static_cast(ElemOffset)) != typeAtPositionOfToken(T, TI)) { return false; } } // Check the index of next expected element for position \c I. if (InputNextPos[I] >= TL) { return false; } // Check the registered *slave* at position \c I. const auto &Slave = Slaves[I]; // If \c Slave is empty, nothing to check. if (!Slave) continue; // Prepare master-output related info for the *slave*. const Token MT = MasterOutputTypes[I]; const bool hasMT = !emptyToken(MT); // \c Slave is not empty here. // Check the `OutputType` and `MasterInputType` of the registered *slave*. const auto &A = unwrapAgent(*Slave); if (!((A.Kind == atoms::SensorKind && static_cast(A).OutputType == T && (!hasMT || static_cast(A).MasterInputType == MT)) || (A.Kind == atoms::AgentKind && static_cast(A).OutputType == T && (!hasMT || static_cast(A).MasterInputType == MT)))) { return false; } // Validate that the *slave* is not registered more than once. if (std::any_of( Slaves.begin() + I + 1, Slaves.end(), [&Slave](const Optional &O) { return O && *Slave == *O; })) { return false; } // Build the content of \c RefIds. RefIds.emplace(A.Id, I); } // Validate *slave* reverse lookup information against our reference. if (RefIds != SlaveIds) { return false; } // Check the size of the master-input storage. if (MasterInputValue->size() != lengthOfToken(MasterInputType)) { return false; } // Check the index of next expected element from the *master*. const token_size_t MITL = lengthOfToken(MasterInputType); if ((MITL != 0 && MasterInputNextPos >= MITL) || (MITL == 0 && MasterInputNextPos != 0)) { return false; } // All checks were successful, the invariant is held. return true; } DeluxeAgent::~DeluxeAgent(void) noexcept { ASSERT(inv()); LOG_TRACE_STREAM << "Destroying DeluxeAgent " << FullName << "..." << std::endl; // Make sure \p this object is not a registered *slave*. if (Master) { ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check. DeluxeAgent &M = static_cast(unwrapAgent(*Master)); ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check. M.registerSlave(M.positionOfSlave(self()), {}); Master = {}; } // Also, make sure \p this object is no acting *master*. for (size_t Pos = 0; Pos < NumberOfInputs; ++Pos) { registerSlave(Pos, {}); } // Now there is no connection with other entities, safe to destroy. } id_t DeluxeAgent::masterId(void) const noexcept { ASSERT(inv() && Master); return unwrapAgent(*Master).Id; } +const DeluxeExecutionPolicy &DeluxeAgent::executionPolicy(void) const noexcept { + ASSERT(inv()); + return *ExecutionPolicy; +} + +bool DeluxeAgent::setExecutionPolicy( + std::unique_ptr &&EP) noexcept { + ASSERT(inv()); + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " setting execution policy " + << *EP << std::endl; + bool Success = false; + // \note The \c rosa::System the \c rosa::Unit is created with is a + // \c rosa::DeluxeSystem. + const DeluxeSystem &DS = static_cast(Unit::system()); + if (EP && EP->canHandle(self(), DS)) { + ExecutionPolicy.swap(EP); + Success = true; + } else { + LOG_TRACE_STREAM << "Execution policy " << *EP + << " cannot handle DeluxeAgent " << FullName << std::endl; + } + ASSERT(inv()); + return Success; +} + Optional DeluxeAgent::master(void) const noexcept { ASSERT(inv()); return Master; } void DeluxeAgent::registerMaster(const Optional _Master) noexcept { ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind)); Master = _Master; ASSERT(inv()); } Token DeluxeAgent::inputType(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return InputTypes[Pos]; } Token DeluxeAgent::masterOutputType(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfMasterOutputs); return MasterOutputTypes[Pos]; } Optional DeluxeAgent::slave(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return Slaves[Pos]; } void DeluxeAgent::registerSlave(const size_t Pos, const Optional Slave) noexcept { ASSERT(inv() && Pos < NumberOfInputs && (!Slave || (unwrapAgent(*Slave).Kind == atoms::SensorKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos] && (emptyToken(MasterOutputTypes[Pos]) || static_cast(unwrapAgent(*Slave)) .MasterInputType == MasterOutputTypes[Pos])) || (unwrapAgent(*Slave).Kind == atoms::AgentKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos] && (emptyToken(MasterOutputTypes[Pos]) || static_cast(unwrapAgent(*Slave)) .MasterInputType == MasterOutputTypes[Pos])))); // If registering an actual *slave*, not just clearing the slot, make sure // the same *slave* is not registered to another slot. if (Slave) { auto It = SlaveIds.find(unwrapAgent(*Slave).Id); if (It != SlaveIds.end()) { Slaves[It->second] = {};//Optional(); SlaveIds.erase(It); } } // Obtain the place whose content is to be replaced with \p Slave auto &OldSlave = Slaves[Pos]; // If there is already a *slave* registered at \p Pos, clear reverse lookup // information for it, and make sure it no longer has \p this object as // *master*. if (OldSlave) { auto &A = unwrapAgent(*OldSlave); ASSERT(SlaveIds.find(A.Id) != SlaveIds.end()); // Sanity check. SlaveIds.erase(A.Id); if (A.Kind == atoms::AgentKind) { static_cast(A).registerMaster({}); } else { ASSERT(A.Kind == atoms::SensorKind); // Sanity check. static_cast(A).registerMaster({}); } } // Register \p Slave at \p Pos. OldSlave = Slave; // If registering an actual *slave*, not just clearing the slot, register // reverse lookup information for the new *slave*. if (Slave) { SlaveIds.emplace(unwrapAgent(*Slave).Id, Pos); } ASSERT(inv()); } size_t DeluxeAgent::positionOfSlave(const AgentHandle Slave) const noexcept { ASSERT(inv()); bool Found = false; size_t Pos = 0; while (!Found && Pos < NumberOfInputs) { auto &ExistingSlave = Slaves[Pos]; if (ExistingSlave && *ExistingSlave == Slave) { Found = true; } else { ++Pos; } } ASSERT(Found || Pos == NumberOfInputs); // Sanity check. return Pos; } void DeluxeAgent::handleTrigger(atoms::Trigger) noexcept { ASSERT(inv()); FP(); ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeContext.cpp b/lib/deluxe/DeluxeContext.cpp index 175e321..7991c03 100755 --- a/lib/deluxe/DeluxeContext.cpp +++ b/lib/deluxe/DeluxeContext.cpp @@ -1,155 +1,201 @@ //===-- deluxe/DeluxeContext.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeContext.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Implementation for rosa/deluxe/DeluxeContext.hpp. /// //===----------------------------------------------------------------------===// #define ROSA_LIB_DELUXE_DELUXECONTEXT_CPP // For including helper macros. #include "rosa/deluxe/DeluxeContext.hpp" #include namespace rosa { namespace deluxe { std::unique_ptr DeluxeContext::create(const std::string &Name) noexcept { return std::unique_ptr(new DeluxeContext(Name)); } DeluxeContext::DeluxeContext(const std::string &Name) noexcept : System(DeluxeSystem::createSystem(Name)) { LOG_TRACE("DeluxeContext for '" + System->name() + "' is created."); } DeluxeContext::~DeluxeContext(void) noexcept { // \c rosa::deluxe::DeluxeContext::System is not used outside, just clean it. for(auto U : DeluxeUnits) { System->destroyAgent(U); } // \note \c System will be marked clean by SystemImpl::~SystemImpl. LOG_TRACE("DeluxeContext for '" + System->name() + "' prepared for destruction."); } +Optional +DeluxeContext::getExecutionPolicy(AgentHandle Unit) const noexcept { + if (System->isDeluxeSensor(Unit)) { + return {System->getDeluxeSensor(Unit)->executionPolicy()}; + } else if (System->isDeluxeAgent(Unit)) { + return {System->getDeluxeAgent(Unit)->executionPolicy()}; + } else { + return {}; + } +} + +DeluxeContext::ErrorCode DeluxeContext::setExecutionPolicy( + AgentHandle Unit, + std::unique_ptr &&ExecutionPolicy) noexcept { + // Generate trace log. + auto &Trace = LOG_TRACE_STREAM; + Trace << "Setting execution policy of " << System->unwrapAgent(Unit).FullName + << " to "; + if (ExecutionPolicy) { + Trace << "'" << ExecutionPolicy->dump() << "'\n"; + } else { + Trace << "[]\n"; + DCRETERROR(ErrorCode::UnsuitableExecutionPolicy); + } + + if (System->isDeluxeSensor(Unit)) { + const bool Success = System->getDeluxeSensor(Unit)->setExecutionPolicy( + std::move(ExecutionPolicy)); + if (!Success) { + DCRETERROR(ErrorCode::UnsuitableExecutionPolicy); + } else { + return ErrorCode::NoError; + } + } else if (System->isDeluxeAgent(Unit)) { + const bool Success = System->getDeluxeAgent(Unit)->setExecutionPolicy( + std::move(ExecutionPolicy)); + if (!Success) { + DCRETERROR(ErrorCode::UnsuitableExecutionPolicy); + } else { + return ErrorCode::NoError; + } + } else { + DCRETERROR(ErrorCode::NotUnit); + } +} + DeluxeContext::ErrorCode DeluxeContext::connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description) noexcept { // Generate trace log. auto &Trace = LOG_TRACE_STREAM; Trace << "Establishing connection"; if (!Description.empty()) { Trace << " '" << Description << "'"; } Trace << " between '" << System->unwrapAgent(Sensor).FullName << "' and '" << System->unwrapAgent(Agent).FullName << "'\n"; // Make sure preconditions are met. if (!System->isDeluxeAgent(Agent)) { DCRETERROR(ErrorCode::NotAgent); } else if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto A = System->getDeluxeAgent(Agent); auto S = System->getDeluxeSensor(Sensor); ASSERT(A && S); // Sanity check. if (Pos >= A->NumberOfInputs) { DCRETERROR(ErrorCode::WrongPosition); } else if (A->inputType(Pos) != S->OutputType || (!emptyToken(A->masterOutputType(Pos)) && A->masterOutputType(Pos) != S->MasterInputType)) { DCRETERROR(ErrorCode::TypeMismatch); } else if (A->slave(Pos)) { DCRETERROR(ErrorCode::AlreadyHasSlave); } else if (S->master()) { DCRETERROR(ErrorCode::AlreadyHasMaster); } // Do register. A->registerSlave(Pos, {Sensor}); S->registerMaster({Agent}); return ErrorCode::NoError; } DeluxeContext::ErrorCode DeluxeContext::connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description) noexcept { // Generate trace log. auto &Trace = LOG_TRACE_STREAM; Trace << "Establishing connection"; if (!Description.empty()) { Trace << " '" << Description << "'"; } Trace << " between '" << System->unwrapAgent(Slave).FullName << "' and '" << System->unwrapAgent(Master).FullName << "'\n"; // Make sure preconditions are met. if (!(System->isDeluxeAgent(Master) && System->isDeluxeAgent(Slave))) { DCRETERROR(ErrorCode::NotAgent); } auto M = System->getDeluxeAgent(Master); auto S = System->getDeluxeAgent(Slave); ASSERT(M && S); // Sanity check. if (Pos >= M->NumberOfInputs) { DCRETERROR(ErrorCode::WrongPosition); } else if (M->inputType(Pos) != S->OutputType || (!emptyToken(M->masterOutputType(Pos)) && M->masterOutputType(Pos) != S->MasterInputType)) { DCRETERROR(ErrorCode::TypeMismatch); } else if (M->slave(Pos)) { DCRETERROR(ErrorCode::AlreadyHasSlave); } else if (S->master()) { DCRETERROR(ErrorCode::AlreadyHasMaster); } // Do register. M->registerSlave(Pos, {Slave}); S->registerMaster({Master}); return ErrorCode::NoError; } std::weak_ptr DeluxeContext::getSystem(void) const noexcept { return std::weak_ptr(System); } void DeluxeContext::initializeSimulation(void) noexcept { // Clear simulation data sources from sensors. for (auto U : DeluxeUnits) { if (auto S = System->getDeluxeSensor(U)) { S->clearSimulationDataSource(); } } } void DeluxeContext::simulate(const size_t NumCycles) const noexcept { ASSERT(std::all_of( DeluxeUnits.begin(), DeluxeUnits.end(), [&](const AgentHandle &H) { return System->isDeluxeAgent(H) || System->isDeluxeSensor(H) && System->getDeluxeSensor(H)->simulationDataSourceIsSet(); })); for (size_t I = 1; I <= NumCycles; ++I) { LOG_TRACE("Simulation cycle: " + std::to_string(I)); for (auto U : DeluxeUnits) { U.sendMessage(Message::create(atoms::Trigger::Value)); } } } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeExecutionPolicy.cpp b/lib/deluxe/DeluxeExecutionPolicy.cpp new file mode 100644 index 0000000..bbea1ea --- /dev/null +++ b/lib/deluxe/DeluxeExecutionPolicy.cpp @@ -0,0 +1,67 @@ +//===-- deluxe/DeluxeExecutionPolicy.cpp ------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/DeluxeExecutionPolicy.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for rosa/deluxe/DeluxeExecutionPolicy.h. +/// +//===----------------------------------------------------------------------===// + +#include "rosa/deluxe/DeluxeExecutionPolicy.h" +#include "rosa/deluxe/DeluxeSystem.hpp" + +#include "executionpolicies/Decimation.h" +#include "executionpolicies/AwaitAll.h" +#include "executionpolicies/AwaitAny.h" + +namespace rosa { +namespace deluxe { + +std::unique_ptr +DeluxeExecutionPolicy::decimation(const size_t D) { + return std::unique_ptr(new Decimation(D)); +} + +std::unique_ptr +DeluxeExecutionPolicy::awaitAll(const std::set &S) { + return std::unique_ptr(new AwaitAll(S)); +} + +std::unique_ptr +DeluxeExecutionPolicy::awaitAny(const std::set &S) { + return std::unique_ptr(new AwaitAny(S)); +} + +bool DeluxeExecutionPolicy::isDeluxeAgent(const AgentHandle H, const DeluxeSystem &S) const noexcept { + return S.isDeluxeAgent(H); +} + +size_t DeluxeExecutionPolicy::numberOfDeluxeAgentInputs( + const AgentHandle H, const DeluxeSystem &S) const noexcept { + auto A = S.getDeluxeAgent(H); + return A ? A->NumberOfInputs : 0; +} + +} // End namespace deluxe +} // End namespace rosa + +namespace std { + +string to_string(const rosa::deluxe::DeluxeExecutionPolicy &EP) { + return EP.dump(); +} + +ostream &operator<<(ostream &OS, + const rosa::deluxe::DeluxeExecutionPolicy &EP) { + OS << to_string(EP); + return OS; +} + +} // End namespace std diff --git a/lib/deluxe/DeluxeSensor.cpp b/lib/deluxe/DeluxeSensor.cpp old mode 100755 new mode 100644 index 32b936b..90eeafe --- a/lib/deluxe/DeluxeSensor.cpp +++ b/lib/deluxe/DeluxeSensor.cpp @@ -1,75 +1,132 @@ //===-- deluxe/DeluxeSensor.cpp ---------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeSensor.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Implementation of rosa/deluxe/DeluxeSensor.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/deluxe/DeluxeSensor.hpp" -#include "rosa/deluxe/DeluxeAgent.hpp" +#include "rosa/deluxe/DeluxeSystem.hpp" namespace rosa { namespace deluxe { +bool DeluxeSensor::inv(void) const noexcept { + // Check execution policy. + // \note The \c rosa::System the \c rosa::Unit is created with is a + // \c rosa::DeluxeSystem. + const DeluxeSystem &DS = static_cast(Unit::system()); + if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) { + return false; + } + + // Check the index of next expected element from the *master*. + const token_size_t MITL = lengthOfToken(MasterInputType); + if ((MITL != 0 && MasterInputNextPos >= MITL) || + (MITL == 0 && MasterInputNextPos != 0)) { + return false; + } + + // All checks were successful, the invariant is held. + return true; +} + id_t DeluxeSensor::masterId(void) const noexcept { ASSERT(Master); return unwrapAgent(*Master).Id; } DeluxeSensor::~DeluxeSensor(void) noexcept { + ASSERT(inv()); LOG_TRACE_STREAM << "Destroying DeluxeSensor " << FullName << "..." << std::endl; // Make sure \p this object is not a registered *slave*. if (Master) { ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check. DeluxeAgent &M = static_cast(unwrapAgent(*Master)); ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check. M.registerSlave(M.positionOfSlave(self()), {}); Master = {}; } } +const DeluxeExecutionPolicy &DeluxeSensor::executionPolicy(void) const + noexcept { + ASSERT(inv()); + return *ExecutionPolicy; +} + +bool DeluxeSensor::setExecutionPolicy( + std::unique_ptr &&EP) noexcept { + ASSERT(inv()); + LOG_TRACE_STREAM << "DeluxeSensor " << FullName + << " setting execution policy " << *EP << std::endl; + bool Success = false; + // \note The \c rosa::System the \c rosa::Unit is created with is a + // \c rosa::DeluxeSystem. + const DeluxeSystem &DS = static_cast(Unit::system()); + if (EP && EP->canHandle(self(), DS)) { + ExecutionPolicy.swap(EP); + Success = true; + } else { + LOG_TRACE_STREAM << "Execution policy " << *EP + << " cannot handle DeluxeSensor " << FullName << std::endl; + } + ASSERT(inv()); + return Success; +} + Optional DeluxeSensor::master(void) const noexcept { + ASSERT(inv()); return Master; } void DeluxeSensor::registerMaster(const Optional _Master) noexcept { - ASSERT(!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind); + ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind)); Master = _Master; + + ASSERT(inv()); } void DeluxeSensor::clearSimulationDataSource(void) noexcept { + ASSERT(inv()); SFP = nullptr; + ASSERT(inv()); } bool DeluxeSensor::simulationDataSourceIsSet(void) const noexcept { + ASSERT(inv()); return SFP != nullptr; } void DeluxeSensor::handleTrigger(atoms::Trigger) noexcept { - ASSERT(MasterInputNextPos == 0); + ASSERT(inv() && MasterInputNextPos == 0); // Process master-input. MFP(); // Obtain the next sensory value. + // \note Execution policy is respected within the data source function. + // Use \c rosa::deluxe::DeluxeSensor::SFP if set, otherwise // \c rosa::deluxe::DeluxeSensor::FP. const H &F = SFP ? SFP : FP; F(); + + ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/executionpolicies/AwaitAll.cpp b/lib/deluxe/executionpolicies/AwaitAll.cpp new file mode 100644 index 0000000..70740b2 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitAll.cpp @@ -0,0 +1,34 @@ +//===-- deluxe/executionpolicies/AwaitAll.cpp -------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitAll.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for deluxe/executionpolicies/AwaitAll.h. +/// +//===----------------------------------------------------------------------===// + +#include "AwaitAll.h" + +#include + +namespace rosa { +namespace deluxe { + +AwaitAll::AwaitAll(const std::set &S) + : AwaitBase(S, + CheckerType(std::all_of::const_iterator, + std::function>)) {} + +std::string AwaitAll::dump(void) const noexcept { + return "Await all of " + dumpS(); +} + +} // End namespace deluxe +} // End namespace rosa diff --git a/lib/deluxe/executionpolicies/AwaitAll.h b/lib/deluxe/executionpolicies/AwaitAll.h new file mode 100644 index 0000000..1374f90 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitAll.h @@ -0,0 +1,48 @@ +//===-- deluxe/executionpolicies/AwaitAll.h ---------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitAll.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Declaration of the *execution policy* *await all*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H +#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H + +#include "AwaitBase.h" + +namespace rosa { +namespace deluxe { + +/// Implementation of the *execution policy* *await all*. +/// +/// \see \c rosa::deluxe::DeluxeExecutionPolicy::awaitAll() +class AwaitAll : public AwaitBase { +public: + /// Constructor. + /// + /// The constructor instatiates \c rosa::deluxe::AwaitBase so that execution + /// is allowed when all *slave* positions included in \p S have received new + /// input since the last triggering. + /// + /// \param S set of *slave* positoins to check + AwaitAll(const std::set &S); + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + std::string dump(void) const noexcept override; +}; + +} // End namespace deluxe +} // End namespace rosa + +#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H diff --git a/lib/deluxe/executionpolicies/AwaitAny.cpp b/lib/deluxe/executionpolicies/AwaitAny.cpp new file mode 100644 index 0000000..109c4cd --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitAny.cpp @@ -0,0 +1,34 @@ +//===-- deluxe/executionpolicies/AwaitAny.cpp -------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitAny.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for deluxe/executionpolicies/AwaitAny.h. +/// +//===----------------------------------------------------------------------===// + +#include "AwaitAny.h" + +#include + +namespace rosa { +namespace deluxe { + +AwaitAny::AwaitAny(const std::set &S) + : AwaitBase(S, + CheckerType(std::any_of::const_iterator, + std::function>)) {} + +std::string AwaitAny::dump(void) const noexcept { + return "Await any of " + dumpS(); +} + +} // End namespace deluxe +} // End namespace rosa diff --git a/lib/deluxe/executionpolicies/AwaitAny.h b/lib/deluxe/executionpolicies/AwaitAny.h new file mode 100644 index 0000000..d69df29 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitAny.h @@ -0,0 +1,48 @@ +//===-- deluxe/executionpolicies/AwaitAny.h ---------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitAny.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Declaration of the *execution policy* *await any*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H +#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H + +#include "AwaitBase.h" + +namespace rosa { +namespace deluxe { + +/// Implementation of the *execution policy* *await any*. +/// +/// \see \c rosa::deluxe::DeluxeExecutionPolicy::awaitAny() +class AwaitAny : public AwaitBase { +public: + /// Constructor. + /// + /// The constructor instatiates \c rosa::deluxe::AwaitBase so that execution + /// is allowed when any of the *slave* positions included in \p S has received + /// new input since the last triggering. + /// + /// \param S set of *slave* positoins to check + AwaitAny(const std::set &S); + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + std::string dump(void) const noexcept override; +}; + +} // End namespace deluxe +} // End namespace rosa + +#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H diff --git a/lib/deluxe/executionpolicies/AwaitBase.cpp b/lib/deluxe/executionpolicies/AwaitBase.cpp new file mode 100644 index 0000000..d4ec684 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitBase.cpp @@ -0,0 +1,60 @@ +//===-- deluxe/executionpolicies/AwaitBase.cpp ------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitBase.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for deluxe/executionpolicies/AwaitBase.h. +/// +//===----------------------------------------------------------------------===// + +#include "AwaitBase.h" +#include "rosa/support/debug.hpp" + +#include +#include + +namespace rosa { +namespace deluxe { + +AwaitBase::AwaitBase(const std::set &S, CheckerType &&Checker) + : Set(S), Checker(Checker) {} + +bool AwaitBase::canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept { + return isDeluxeAgent(H, S) && + canHandleNumberOfInputs(numberOfDeluxeAgentInputs(H, S)); +} + +bool AwaitBase::shouldProcess(const std::vector &InputChanged) noexcept { + // Sanity check of usage. + ASSERT(canHandleNumberOfInputs(InputChanged.size())); + return Checker(Set.begin(), Set.end(), + [&InputChanged](const size_t I) { return InputChanged[I]; }); +} + +bool AwaitBase::canHandleNumberOfInputs(const size_t NumberOfInputs) const + noexcept { + const auto MaxElemIt = std::max_element(Set.begin(), Set.end()); + const size_t MaxElem = (MaxElemIt == Set.end()) ? 0 : *MaxElemIt; + return MaxElem <= NumberOfInputs; +} + +std::string AwaitBase::dumpS(void) const noexcept { + std::stringstream SS; + SS << "["; + for (const auto &Value : Set) { + SS << " " << Value; + } + SS << " ]"; + return SS.str(); +} + +} // End namespace deluxe +} // End namespace rosa diff --git a/lib/deluxe/executionpolicies/AwaitBase.h b/lib/deluxe/executionpolicies/AwaitBase.h new file mode 100644 index 0000000..e196701 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitBase.h @@ -0,0 +1,105 @@ +//===-- deluxe/executionpolicies/AwaitBase.h --------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitBase.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Declaration of an *execution policy* that makes decisions depending +/// on input state of *slave* positions. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H +#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H + +#include "rosa/deluxe/DeluxeExecutionPolicy.h" + +#include + +namespace rosa { +namespace deluxe { + +/// Implementation of an *execution policy* that makes decisions depending on +/// whether new input has been received at some *slave* positions since the +/// laste triggering. +/// +/// The class implements the \c rosa::deluxe::DeluxeExecutionPolicy interface +/// but delegates the defintion of the actual decision-making function to +/// subclasses. +/// +/// \see rosa::deluxe::AwaitAll +/// \see rosa::deluxe::AwaitAny +class AwaitBase : public DeluxeExecutionPolicy { +protected: + /// Set of *slave* positions to check. + const std::set Set; + + /// Type of decision-making function used in \c + /// rosa::deluxe::AwaitBase::shouldProcess(). + using CheckerType = std::function::const_iterator, + std::set::const_iterator, + std::function)>; + + // Decision-making function for \c rosa::deluxe::AwaitBase::shouldProcess(). + const CheckerType Checker; + + /// Protected constructor, only subclasses can instatiate the class. + /// + /// \param S set of *slave* positions to await input from + /// \param Checker function that decides about execution + AwaitBase(const std::set &S, CheckerType &&Checker); + +public: + /// Tells if \p this object can handle the deluxe *unit* referred by \p H. + /// + /// Any *execution policy* based on this class can handle *agents* with at + /// least as many *slave* positions as the largest one defined in \c + /// rosa::deluxe::AwaitBase::Set. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if \p this object can handle the *unit* referred by \p H + bool canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept override; + + /// Tells if processing function should be executed on the current triggering. + /// + /// Waiting for input allows execution when \c + /// rosa::deluxe::AwaitBase::Checker evaluates to \c true with respect to \c + /// rosa::deluxe::AwaitBase::Set and \p InputChanged. + /// + /// \param InputChanged flags indicating whether new input has been received + /// at *slave* positions + /// + /// \return if to execute processing function + bool shouldProcess(const std::vector &InputChanged) noexcept override; + +private: + /// Tells if \p this object can handle a *unit* with \p NumberOfInputs *slave* + /// positions. + /// + /// \param NumberOfInputs the number of *slave* positions to consider + /// + /// \return if \p this object can handle a *unit* with \p NumberOfInputs + /// *slave* positions + bool canHandleNumberOfInputs(const size_t NumberOfInputs) const noexcept; + +protected: + /// Dumps the set of *slave* positions that \p this object checks. + /// + /// \return textual representation of \c rosa::deluxe::AwaitBase::Set of \p + /// this object + std::string dumpS(void) const noexcept; +}; + +} // End namespace deluxe +} // End namespace rosa + +#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H diff --git a/lib/deluxe/executionpolicies/Decimation.cpp b/lib/deluxe/executionpolicies/Decimation.cpp new file mode 100644 index 0000000..2f324f9 --- /dev/null +++ b/lib/deluxe/executionpolicies/Decimation.cpp @@ -0,0 +1,41 @@ +//===-- deluxe/executionpolicies/Decimation.cpp -----------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/Decimation.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for deluxe/executionpolicies/Decimation.h. +/// +//===----------------------------------------------------------------------===// + +#include "Decimation.h" + +#include + +namespace rosa { +namespace deluxe { + +Decimation::Decimation(const size_t D) + : Rate(std::max(D, 1)), Cycle(0) {} + +bool Decimation::canHandle(const AgentHandle, const DeluxeSystem &) const + noexcept { + return true; +} + +bool Decimation::shouldProcess(const std::vector &) noexcept { + return (Cycle++ % Rate) == 0; +} + +std::string Decimation::dump(void) const noexcept { + return "Decimation with rate " + std::to_string(Rate); +} + +} // End namespace deluxe +} // End namespace rosa diff --git a/lib/deluxe/executionpolicies/Decimation.h b/lib/deluxe/executionpolicies/Decimation.h new file mode 100644 index 0000000..b4d826c --- /dev/null +++ b/lib/deluxe/executionpolicies/Decimation.h @@ -0,0 +1,73 @@ +//===-- deluxe/executionpolicies/Decimation.h -------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/Decimation.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Declaration of the *execution policy* *decimation*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H +#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H + +#include "rosa/deluxe/DeluxeExecutionPolicy.h" + +namespace rosa { +namespace deluxe { + +/// Implementation of the *execution policy* *decimation*. +/// +/// \see \c rosa::deluxe::DeluxeExecutionPolicy::decimation() +class Decimation : public DeluxeExecutionPolicy { + + /// The rate of *decimation*. + const size_t Rate; + + /// Counter of triggerings. + size_t Cycle; + +public: + /// Constructor. + /// + /// \param D the rate of *decimation* + Decimation(const size_t D); + + /// Tells if \p this object can handle the deluxe *unit* referred by \p H. + /// + /// *Decimation* can handle any *units*. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if \p this object can handle the *unit* referred by \p H + bool canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept override; + + /// Tells if processing function should be executed on the current triggering. + /// + /// *Decimation* allows execution on each \c rosa::deluxe::Decimation::Rate + /// th triggering (i.e., calling of the function), which is counted + /// by \p this object in \c rosa::deluxe::Decimation::Cycle. + /// + /// \param InputChanged *ignored* + /// + /// \return if to execute processing function + bool shouldProcess(const std::vector &InputChanged) noexcept override; + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + std::string dump(void) const noexcept override; +}; + +} // End namespace deluxe +} // End namespace rosa + +#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt new file mode 100644 index 0000000..6979d5e --- /dev/null +++ b/modules/CMakeLists.txt @@ -0,0 +1,19 @@ +if ( ROSA_COMPILER_IS_GCC_COMPATIBLE ) + + # Allow exceptions by removing restricting flag (needed by cxxopts) + remove("-fno-exceptions" CMAKE_CXX_FLAGS) + + # Allow dynamic casts by removing restriction flag (needed by cxxopts) + remove("-fno-rtti" CMAKE_CXX_FLAGS) +elseif ( MSVC ) + + # Exceptions enabled for MSVC by default but need to allow RTTI + # (needed by cxxopts) + remove("/GR-" CMAKE_CXX_FLAGS) +endif() + +execute_process(COMMAND git submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# Add the different subdirectories +ADDALLSUBDIRS() diff --git a/modules/cxxopts b/modules/cxxopts new file mode 160000 index 0000000..a0de9f3 --- /dev/null +++ b/modules/cxxopts @@ -0,0 +1 @@ +Subproject commit a0de9f3ba1035a3c4f5ffcd960cb94e4e12d40c5 diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 813fd09..d00c89d 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,2 +1,7 @@ +# Allow exceptions by removing restricting flag. +if ( ROSA_COMPILER_IS_GCC_COMPATIBLE ) + remove("-fno-exceptions" CMAKE_CXX_FLAGS) +endif() + # Add the different subdirectories ADDALLSUBDIRS()