diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp index a4d5b51..5fc6075 100644 --- a/examples/CSVFiles/main.cpp +++ b/examples/CSVFiles/main.cpp @@ -1,360 +1,360 @@ //===-- examples/CSVFiles/main.cpp ------------------*- 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 examples/basic-system/basic-system.cpp /// /// \author Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2019 /// /// \brief A simple example on the basic \c rosa::csv, \c rosa::iterator and /// \c rosa::writer classes. Focus is on the tuple impementations. /// //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include #include #include #include //includes for an complete example to read and write //with sensors and agents. #include "rosa/deluxe/DeluxeContext.hpp" #include "rosa/config/version.h" //includes to test the basic functionality //to read and write tuples. #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include "rosa/support/iterator/split_tuple_iterator.hpp" #include "rosa/support/writer/split_tuple_writer.hpp" /// the name of the example const std::string ExampleName = "csvfiles"; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 10; /// Paths for the CSV files for simulation. /// input csv files const std::string csvPath = "../examples/CSVFiles/"; const std::string csvFileWithHeader = csvPath + "HR-New.csv"; const std::string csvFileNoHeader = csvPath + "HR.csv"; const std::string csvFileHeaderSemi = csvPath + "HR-New-Semicolon.csv"; /// output csv files const std::string csvFileWriteHea = csvPath + "csvwriter_noheader.csv"; const std::string csvFileWriteNoHeaSplit = csvPath + "csvSplitwriter_noheader.csv"; using namespace rosa; /// /// This function tests the basic CSVIterator capablities, and shows you /// how you could work with this class. /// void testtupleCSVReader(void){ //different streams to get the csv data out of the files //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_2(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_3(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_4(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_5(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_6(csvFileWithHeader); //file contains no header an valid data entries, delimter = ',' std::ifstream file2(csvFileNoHeader); //file contains header and valid data entries, delimter = ';' std::ifstream file3(csvFileHeaderSemi); csv::CSVIterator it(file_header_data); it.setDelimeter(','); - it++; - it++; + (void)++it; + (void)++it; //if you iterate over the end of file, the last values //of the file will remain in the data structure but no //error occurs. - it++; - it++; + (void)++it; + (void)++it; //------------------------------------------------------------------- // a possiblity to get the data out of the iterator std::tuple value = *it; // // Show the value of one iterator // LOG_INFO( "Values are: "); LOG_INFO(std::get<0>(value) ); LOG_INFO(std::get<1>(value) ); //-------------------------------------------------------------------- //testing differnet parameters to the constructor //uncomment to see that it is not possible to iterate over an vector in the tuple. //rosa::csv::CSVIterator> it2(file, 1); //try to skip a valid number of lines after the header csv::CSVIterator it2_0(file_header_data_2, 1); //try to skip a valid number of lines after the header, but you assume that the file has no header //uncomment this line to crash the programm //csv::CSVIterator it2_1(file_header_data_3, 0, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines after the header, but you assume that the file has no header //uncomment this line to crash the program //csv::CSVIterator it2_2(file_header_data_4, 1, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines of a file without header csv::CSVIterator it2_3(file2, 1, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines after the header, but with different delimeter csv::CSVIterator it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';'); // if you skip more lines than valid, you generate an infinte loop //csv::CSVIterator it3(file_header_data_5, 500); //if you don't need data from all columns just select the number of columns you //need. You get the data back from the first column (index 0) to the fourth column //all values from the fifth column are ignored. csv::CSVIterator it4(file_header_data_6); } /// /// This function tests the basic CSVTupleWriter capablities, and shows you /// how you could work with this class. /// void testtupleCSVWriter(void){ // // Create output writer with an file // std::ofstream file_header_out(csvFileWriteHea); csv::CSVTupleWriter wri(file_header_out); // // Create test tuples // std::tuple values(5, 8.3f, "hallo"); std::tuple values2(3, 8.3f, "end"); // // Create test header lines for the test tuples // std::array header{ {"zero column", "first column", "second column"}}; std::array headerWrong{ {"zero column", "first column", "second column", "third column"}}; std::array headerWrongShort{ {"zero column", "first column"}}; //if you uncomment this line than it would be possible for you to write the header into the stream //in the next line. //wri.write(values); wri.writeHeader(header); wri.write(values); wri.write(values); // it is not possible to write an additional header into the stream. wri.writeHeader(header); wri.write(values); wri << values; wri << values2; //uncomment this line to see, that you can't write a header with the too many elements. //wri.writeHeader(headerWrong); //uncomment this line to see, that you can't write a header with the too few elements. //wri.writeHeader(headerWrongShort); } /// /// This function tests the basic splitTupleIterator capablities, and shows you /// how you could work with this class, this class is used if you want to split /// a CSVIterator in separate parts. /// void testsplitTupleIterator(void) { // // Create deluxe context // std::unique_ptr C = deluxe::DeluxeContext::create(ExampleName); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the second argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. // Three different sensors were created, this is just a random number taken. AgentHandle Elem0Sensor = C->createSensor("Element1 Sensor"); AgentHandle Elem1Sensor = C->createSensor("Element2 Sensor"); AgentHandle Elem2Sensor = C->createSensor("Element3 Sensor"); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // Type aliases for iterators using Iterator = rosa::csv::CSVIterator; using IteratorValue = std::tuple; static_assert (std::is_same::value, "Iterator must provide tuples" ); // // Open CSV file and register the columns to the corresponding sensors. // std::ifstream TestCSV(csvFileWithHeader); // // Test data looks like: // Element1, Element2, Element3, Element4, Element5 -- is the header line // 3, 5, 8, 9.5, 17 -- first line of values // 100, -8, 30, 18.8, 29 -- other line of values were also in the file // 5, 20, -100, -200.1, -30 -- if you have less number of values than simulation rounds all values // -- beyond your last value will be zero. //get element iterator ranges auto [Elem0Range, Elem1Range, Elem2Range] = iterator::splitTupleIterator(Iterator(TestCSV), Iterator()); //dissect a range into begin and end iterators by structred bindings auto[Elem0Begin, Elem0End] = Elem0Range; //deissect a range with functions auto Elem1Begin = iterator::begin(Elem1Range); auto Elem1End = iterator::end(Elem1Range); C->registerSensorValues(Elem0Sensor, std::move(Elem0Begin), Elem0End); C->registerSensorValues(Elem1Sensor, std::move(Elem1Begin), Elem1End); C->registerSensorValues(Elem2Sensor, std::move(iterator::begin(Elem2Range)), iterator::end(Elem2Range)); // // Simulate. // C->simulate(NumberOfSimulationCycles); } /// /// This function tests the basic splitTupleWriter capablities, and shows you /// how you could work with this class, this class is used if you want to split /// a CSVWriter in separate parts. /// void testsplitTupleWriter(void){ // // Create output writer with an file // std::ofstream file_header_out(csvFileWriteNoHeaSplit); csv::CSVTupleWriter wri(file_header_out); // if you omit, the type definition in the template, than auto generated types were used, // and they may not fit to the used CSVTupleWriter. wri << std::make_tuple(1000, 50.6f, "tuple_created"); auto [T0Writer, T1Writer, T2Writer] = writer::splitTupleWriter(std::move(wri), writer::IncompleteTuplePolicy::Ignore); //writing elements in sequential order into the writer classes, but you can write the values into the writers in //a random order. T0Writer << (500); T1Writer << (3.0); T2Writer << "splitted writter"; T2Writer << "splitting is cool"; T0Writer << (-30); T1Writer << (-0.004f); // you can also write more often values into a writer and later into the other writers // all data will be processed correctly into the right order. T0Writer << (1); T0Writer << (2); T1Writer << (-0.4f); T0Writer << (3); T2Writer << "again"; T0Writer << (4); T1Writer << (-0.1f); T1Writer << (-0.2f); T2Writer << "and"; T1Writer << (-0.3f); T2Writer << "splitting"; T2Writer << "once again"; // again writing data of one tuple entry to the different writers in a random fashion. T1Writer << (-0.004f); T2Writer << "splitting is cool"; T0Writer << (-30); } int main(void){ LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red << ExampleName << " example" << terminal::Color::Default << '\n'; // // Testing CSVWriter. // LOG_INFO("Testing CSVWriter CSVTupleItrator implementation: "); testtupleCSVWriter(); // // Testing CSVReader. // LOG_INFO("Testing CSVReader CSVTupleIterator implementation: "); testtupleCSVReader(); // // Testing SplitTupleIterator. // LOG_INFO("Testing SplitTupleIterator: "); testsplitTupleIterator(); // // Testing SplitTupleWriter. // LOG_INFO("Testing SplitTupleWriter: "); testsplitTupleWriter(); // // info that user knows programm has finished. // LOG_INFO( "All tests finished."); return 0; } diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp index ba521a7..15e9a81 100644 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,674 +1,674 @@ //===-- 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 /// /// \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 /// /// \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__ 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 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); } }; #ifdef __clang__ #pragma clang diagnostic pop #endif // defined __clang__ } 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 - F(); + (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), 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 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