diff --git a/docs/Design.rst b/docs/Design.rst new file mode 100644 index 0000000..4f07a0f --- /dev/null +++ b/docs/Design.rst @@ -0,0 +1,119 @@ +=============== +Design Overview +=============== + +Last updated: 2019-11-27 + +TODO: + +- Add cross references to relevant parts of the documentation and external links (e.g., relevant papers or source of info) + +.. contents:: + :local: + + +Principles +========== + +The main priorities for the design of the software implementation are + +#. providing a high-level, easy-to-use but safe interface for application developers; +#. allowing the same application code to be used for *simulation* and *deployment*; +#. realizing small-footprint software that can be deployed in resource-constrained embedded devices. + +The RoSA software framework, is implemented in C++. +The language allows defining high-level but lightweight interfaces with flexibility in the realization. +The framework heavily utilizes templates, template metaprogramming, and constant expressions to let the compiler generate optimized code. +We also encode dynamically-relevant static information into a custom format so that RoSA supports message passing with flexibility and minimal resource need --- without the dynamic overhead of C++ RTTI. + +Performing message passing and data manipulation is decoupled from the agent interface. +The implementation of the runtime system is easily switched behind the declarative definition of agent-based applications. +Running an application in different modes (e.g., simulation and deployment) is a matter of static configuration. + +Simulation is the execution of the application on a computer with sensor and actuator agents associated to input and output streams, respectively. +Those sensor and actuator agents may interface actual sensors and actuators with custom C++ code when deployed in an embedded device. + +Case studies have been dony in simulation; support for deployment requires further development effort to complete the corresponding runtime implementation. + +The framework is being coded in standard C++. +The only exceptions are some OS-specific code snippets within debugging and simulation facilities. +The software can readily be deployed to all devices that are supported by a modern C++ compiler. + +Software Modules +================ + +The software implementation consists of four main modules: + +`Support` + provides general debugging, logging, typing, and I/O facilities, which are used in other modules and application code. + +`Agent` + is a library of self-aware functionalities for defining agents in application code. + +`Core` + defines a minimal agent system with message passing capabilities and runtime system implementations. + +`Deluxe` + provides an interface that matches the RoSA Architecture and utilizes RoSA Core. + + +The RoSA Deluxe interface wraps the general agent interface of RoSA Core. +RoSA Deluxe agents are defined by functions that take their input messages as parameters and provide their output messages as return values. +The implementation takes care of handling messages --- minimizing boilerplate in application code --- and may perform static and dynamic checks to ensure operating conditions. + +Agents and Agent Systems +------------------------ + +RoSA Core defines the means for agents to receive and send messages. +Messages are statically-typed tuples of basic types. +An agent is defined as a list of message handler functions. +An incoming message is handled by the first handler that can take the message as actual parameter. +The handler may generate output messages via RoSA API calls. + +RoSA Deluxe agents build on RoSA Core agents and provide a higher abstraction level by hiding message passing. + +Each agent is owned by a *system*. +Systems can instantiate agents and provide the actual implementation for execution: channels --- for message passing --- and control --- for triggering data manipulation. + +RoSA Core allows message passing between arbitrary agents within a system. +The RoSA Deluxe interface enforces a hierarchical structure with master-slave relations. + + +A RoSA application is an agent system. +The Deluxe module allows implementing a system as a set of agent definitions and declarative description of their connections. + +Execution Model +--------------- + +How an application (i.e., agent system) is executed depends on the system implementation. +The implementation of the system --- channel and control facilities --- is decoupled from the definition of an application --- agents and their connections within a system. +RoSA Core allows swapping the system implementation for any application as a configuration option or by changing one line of application code. + +The current system implementation is based on Models of Computations (MoCs). +We use the untimed MoC and are prepared for the discrete-timed MoC when time aspects have to be represented. +The concepts of the agent-based approach are easily mapped to the formal MoCs. + +=================== =========== +Agent-based concept MoC concept +=================== =========== +Agent Process +Channel Signal +Message Event +=================== =========== + +A RoSA agent system --- mapping to a process network --- can be analysed in the formal model. + + +Building and Running Applications +--------------------------------- + +RoSA has a CMake build project, which allows using the framework in different development environments. +The build project defines compile-time options to disable or tune features statically (e.g., logging, debugging, and tracing facilities). +Disabling unnecessary features improves the efficiency of compiled framework code in particular uses. +A RoSA application and framework modules are linked into one executable, which allows exploiting link-time optimization possibilities (optimizing framework code for application need, removing unused framework code, etc.). + + +The RoSA runtime system is a thin layer between the self-aware application and the underlying system; still it fulfills important responsibilities. +The runtime system initializes the application by instantiating an agent system, performs message passing, and triggers data manipulation in agents. +I/O and monitoring features are also provided by RoSA. +The application code is not bloated with system-level bookkeeping and developers can focus on details of self-awareness. diff --git a/docs/index.rst b/docs/index.rst index e42d88a..cf61d5a 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,83 +1,86 @@ ================== The RoSA Framework ================== .. contents:: :local: Overview ======== The RoSA (Research on Self-Awareness) Framework is intended to be used as a tool for experimenting and evaluating new ways of implementing self-aware applications. This documentation was generated for RoSA version |release| on |today|. Documentation ============= .. toctree:: :hidden: - Changelog + Design Use Build Dev Issues Release Plan CommandGuide/index +:doc:`Design` + General overview of design principles and decisions. + :doc:`Use` Describes how to create your own software based on RoSA. :doc:`Build` Describes how to build RoSA. :doc:`Dev` Provides basic information for developers on how to contribute to RoSA. :doc:`Issues` Known issues and things to do with the current implementation of RoSA. :doc:`Release` Describes for maintainers how to make a new release of RoSA. :doc:`Plan` Discusses future work and possible ways to develop RoSA. :doc:`CommandGuide/index` Documentation for RoSA applications and tools. Changes ======= Refer to :doc:`Changelog`. .. _API_Documentation: API Documentation ================= For details of the provided interface, refer to our `API documentation`_. .. _API documentation: ../doxygen/html/index.html License ======= RoSA is distributed under the terms and conditions of the `Boost Software License 1.0 `_: .. literalinclude:: ../LICENSE :language: none .. note:: 3rd party externals in the directory `modules` are not part of the RoSA source base and are licensed separately. Indices and Tables ================== * :ref:`genindex` * :ref:`search` diff --git a/include/rosa/config/config.h b/include/rosa/config/config.h index 6090667..7359ccd 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,86 +1,86 @@ //===-- rosa/config/config.h ------------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/config/config.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \brief Configuration information on the build of the library. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_CONFIG_H #define ROSA_CONFIG_CONFIG_H #include "rosa/config/rosa_config.h" #include // This OS-specific block defines one of the following: // - ROSA_LINUX // - ROSA_WINDOWS // It also defines ROSA_POSIX for POSIX-compatible systems. #if defined(__linux__) #define ROSA_LINUX #elif defined(WIN32) || defined(_WIN32) #define ROSA_WINDOWS #else #error Platform and/or compiler not supported #endif #if defined(ROSA_LINUX) #define ROSA_POSIX #endif // Defining filenames in a project-relative way based on absolute paths. #include "rosa/config/project_path.hpp" /// The project-relative path of the current source file. -#define __FILENAME__ (__FILE__ + project_relative_path_index(__FILE__)) +#define __FILENAME__ (project_relative_path(__FILE__)) // Convenience macros. /// No-op. #define ROSA_VOID_STMT static_cast(0) /// Ignors an expression. /// /// \param x expression #define ROSA_IGNORE_UNUSED_EXPR(x) static_cast(x) /// Ignors a statement (not an expression). /// /// \param s statement #define ROSA_IGNORE_UNUSED_STMT(s) \ while (false) { \ s; \ } \ /// Prints an error message and aborts execution. /// /// \param error the error message to print #define ROSA_CRITICAL(error) \ do { \ std::cerr << "[FATAL] " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": critical error: '" << (error) << "'" \ << std::endl; \ ::abort(); \ } while (false) /// Raises a runtime error in the program. /// /// \param msg message describing the error /// /// \throws std::runtime_error #define ROSA_RAISE_ERROR(msg) throw std::runtime_error(msg) #endif // ROSA_CONFIG_CONFIG_H diff --git a/include/rosa/config/project_path.hpp b/include/rosa/config/project_path.hpp index a719fae..046ddfc 100644 --- a/include/rosa/config/project_path.hpp +++ b/include/rosa/config/project_path.hpp @@ -1,72 +1,87 @@ //===-- rosa/config/project_path.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/config/project_path.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2020 /// /// \brief Facility for compile-time manipulation of paths. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_PROJECT_PATH_HPP #define ROSA_CONFIG_PROJECT_PATH_HPP #include "rosa/config/rosa_config.h" +#include "rosa/support/diagnostics.h" #include namespace rosa { /// Nested namespace with implementation of the provided features, /// consider it private. namespace { /// Tells the index of the project-relative part of an absolute path. /// /// \param path absolute path to check /// \param project the absolute path of the project /// \param index number of leading characters already checked and found /// matching /// /// \return index of the part relative to \c project in \p path; \c 0 if \p path /// is not under \p project constexpr size_t project_relative_path_index_impl(const char *const path, const char *const project = ROSA_SRC_DIR, const size_t index = 0) { return project[index] == '\0' ? index // Found it. : (path[index] == '\0' || project[index] != path[index]) ? 0 // Path is not under project. : project_relative_path_index_impl( path, project, index + 1); // Continue searching... } } // End namespace /// Tells the index of the project-relative part of an absolute path. /// /// \param path absolute path to check /// /// \return index of the project-relative part of \p path; \c 0 if \p path is -/// not under the RoSA siource directory. +/// not under the RoSA source directory. constexpr size_t project_relative_path_index(const char *const path) { return project_relative_path_index_impl(path); } +/// Gives the project-relative part of an absolute path. +/// +/// \param path absolute path to check +/// +/// \return the project-relative part of \p path; \c path if it is not under the +/// RoSA source directory. +constexpr const char *project_relative_path(const char *const path) { +// NOTE: Some compilers warn about adding int to string. +ROSA_DISABLE_WARNING_PUSH; +ROSA_DISABLE_WARNING_ADDING_INT_TO_STRING; + return path + project_relative_path_index(path); +ROSA_DISABLE_WARNING_POP; +} + } // End namespace rosa #endif // ROSA_CONFIG_PROJECT_PATH_HPP diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp index 151024c..c4846a6 100644 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,678 +1,676 @@ //===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \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" +#include "rosa/support/diagnostics.h" + /// 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 /// /// \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, 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); // NOTE: Clang 6 warns about unused lambda captures; we suppress that // warning (those variables need to be captured). -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-lambda-capture" -#endif // defined __clang__ +ROSA_DISABLE_WARNING_PUSH; +ROSA_DISABLE_WARNING_UNUSED_LAMBDA_CAPTURE; return [ this, MF ](void) noexcept { // Do not do anything for master-input type \c // rosa::deluxe::EmptyDeluxeTuple. if constexpr(!std::is_same::value) { LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " handles master-input." << std::endl; // The assert must hold if \p this object was successfully constructed. STATIC_ASSERT( (true && ... && (static_cast(static_cast(S0)) == S0)), "Unexpected error"); 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); } }; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif // defined __clang__ +ROSA_DISABLE_WARNING_POP; } template 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, 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 (void)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)), ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), MasterInputType(MT::TT), MasterInputNextPos(0), MasterInputChanged(false), MasterInputValue(new typename TokenizedStorageForTypeList< typename UnwrapDeluxeTuple::Type>::Type()), MFP(triggerHandlerFromProcessingFunction(std::move(MF), seq_t())), 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), 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 successfully constructed. STATIC_ASSERT((true && ... && (static_cast(static_cast(S0)) == S0)), "Unexpected error"); // 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/support/diagnostics.h b/include/rosa/support/diagnostics.h new file mode 100644 index 0000000..99b3fe4 --- /dev/null +++ b/include/rosa/support/diagnostics.h @@ -0,0 +1,55 @@ +//===-- rosa/support/diagnostics.h ------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/diagnostics.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2020 +/// +/// \brief Facility for diagnostics. +/// +//===----------------------------------------------------------------------===// + +#if defined(_MSC_VER) + +#define ROSA_DISABLE_WARNING_PUSH __pragma(warning(push)) +#define ROSA_DISABLE_WARNING_POP __pragma(warning(pop)) + +#define ROSA_DISABLE_WARNING(warningNumber) \ + __pragma(warning(disable : warningNumber)) + +#define ROSA_DISABLE_WARNING_UNUSED_LAMBDA_CAPTURE +#define ROSA_DISABLE_WARNING_ADDING_INT_TO_STRING + +#elif defined(__GNUC__) || defined(__clang__) + +#define _ROSA_DIAG_DO_PRAGMA(X) _Pragma(#X) + +#define ROSA_DISABLE_WARNING_PUSH _ROSA_DIAG_DO_PRAGMA(GCC diagnostic push) +#define ROSA_DISABLE_WARNING_POP _ROSA_DIAG_DO_PRAGMA(GCC diagnostic pop) +#define ROSA_DISABLE_WARNING(warningName) \ + _ROSA_DIAG_DO_PRAGMA(GCC diagnostic ignored warningName) + +#define ROSA_DISABLE_WARNING_UNUSED_LAMBDA_CAPTURE \ + ROSA_DISABLE_WARNING("-Wunused-lambda-capture") +#define ROSA_DISABLE_WARNING_ADDING_INT_TO_STRING \ + ROSA_DISABLE_WARNING("-Wstring-plus-int") + +#else + +#define ROSA_DISABLE_WARNING_PUSH +#define ROSA_DISABLE_WARNING_POP +#define ROSA_DISABLE_WARNING_UNUSED_LAMBDA_CAPTURE +#define ROSA_DISABLE_WARNING_ADDING_INT_TO_STRING + +#endif