Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F480415
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Size
161 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp
index e6f615d..98aa787 100644
--- a/apps/ccam/ccam.cpp
+++ b/apps/ccam/ccam.cpp
@@ -1,279 +1,279 @@
//===-- apps/ccam/ccam.cpp --------------------------------------*- C++ -*-===//
//
// The RoSA Framework -- Application CCAM
//
//===----------------------------------------------------------------------===//
///
/// \file apps/ccam/ccam.cpp
///
/// \author Maximilian Goetzinger (maximilian.goetzinger@tuwien.ac.at)
/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at)
///
/// \date 2019
///
/// \brief The application CCAM implements the case study from the paper:
/// M. Goetzinger, N. TaheriNejad, H. A. Kholerdi, A. Jantsch, E. Willegger,
/// T. Glatzl, A.M. Rahmani, T.Sauter, P. Liljeberg: Model - Free Condition
/// Monitoring with Confidence
//===----------------------------------------------------------------------===//
#include "rosa/agent/Abstraction.hpp"
#include "rosa/agent/Confidence.hpp"
#include "rosa/agent/FunctionAbstractions.hpp"
#include <iostream>
#include "rosa/config/version.h"
#include "rosa/agent/SignalStateDetector.hpp"
#include "rosa/agent/SystemStateDetector.hpp"
#include "rosa/deluxe/DeluxeContext.hpp"
#include "rosa/support/csv/CSVReader.hpp"
#include "rosa/support/csv/CSVWriter.hpp"
#include <fstream>
#include <limits>
#include <memory>
#include <streambuf>
#include "configuration.h"
#include "statehandlerutils.h"
using namespace rosa;
using namespace rosa::agent;
using namespace rosa::deluxe;
using namespace rosa::terminal;
const std::string AppName = "CCAM";
int main(int argc, char **argv) {
LOG_INFO_STREAM << '\n'
<< library_string() << " -- " << Color::Red << AppName
<< "app" << Color::Default << '\n';
if (argc < 2) {
LOG_ERROR("Specify config File!\nUsage:\n\tccam config.json");
return 1;
}
std::string ConfigPath = argv[1];
if (!readConfigFile(ConfigPath)) {
LOG_ERROR_STREAM << "Could not read config from \"" << ConfigPath << "\"\n";
return 2;
}
std::string InputFilePath, OutputFilePath;
LOG_INFO("Creating Context");
std::unique_ptr<DeluxeContext> C = DeluxeContext::create(AppName);
- std::shared_ptr<PartialFunction<float, float>> BrokenDelayFunction(
- new PartialFunction<float, float>(
+ std::shared_ptr<PartialFunction<uint32_t, float>> BrokenDelayFunction(
+ new PartialFunction<uint32_t, float>(
{{{0, AppConfig.BrokenCounter},
- std::make_shared<LinearFunction<float, float>>(
+ std::make_shared<LinearFunction<uint32_t, float>>(
0, 0.f, AppConfig.BrokenCounter, 1.f)},
- {{AppConfig.BrokenCounter, std::numeric_limits<float>::max()},
- std::make_shared<LinearFunction<float, float>>(1.f, 0.f)}},
- 0));
+ {{AppConfig.BrokenCounter, std::numeric_limits<uint32_t>::max()},
+ std::make_shared<LinearFunction<uint32_t, float>>(1.f, 0.f)}},
+ 0.f));
- std::shared_ptr<PartialFunction<float, float>> OkDelayFunction(
- new PartialFunction<float, float>(
+ std::shared_ptr<PartialFunction<uint32_t, float>> OkDelayFunction(
+ new PartialFunction<uint32_t, float>(
{{{0, AppConfig.BrokenCounter},
- std::make_shared<LinearFunction<float, float>>(
+ std::make_shared<LinearFunction<uint32_t, float>>(
0, 1.f, AppConfig.BrokenCounter, 0.f)},
- {{AppConfig.BrokenCounter, std::numeric_limits<float>::max()},
- std::make_shared<LinearFunction<float, float>>(0.f, 0.f)}},
- 1));
+ {{AppConfig.BrokenCounter, std::numeric_limits<uint32_t>::max()},
+ std::make_shared<LinearFunction<uint32_t, float>>(0.f, 0.f)}},
+ 1.f));
//
// Create a DeluxeAgent with SystemStateDetector functionality.
//
LOG_INFO("Create SystemStateDetector agent.");
AgentHandle SystemStateDetectorAgent = createSystemStateDetectorAgent(
C, "SystemStateDetector", AppConfig.SignalConfigurations.size(),
BrokenDelayFunction, OkDelayFunction);
LOG_INFO("Creating sensors, SignalStateDetector functionalities and their "
"Abstractions.");
std::vector<AgentHandle> Sensors;
std::vector<std::shared_ptr<PartialFunction<float, float>>>
SampleMatchesFunctions;
std::vector<std::shared_ptr<PartialFunction<float, float>>>
SampleMismatchesFunctions;
std::vector<std::shared_ptr<PartialFunction<float, float>>>
SignalIsStableFunctions;
std::vector<std::shared_ptr<PartialFunction<float, float>>>
SignalIsDriftingFunctions;
std::vector<std::shared_ptr<StepFunction<float, float>>>
NumOfSamplesMatchFunctions;
std::vector<std::shared_ptr<StepFunction<float, float>>>
NumOfSamplesMismatchFunctions;
std::vector<std::shared_ptr<
SignalStateDetector<float, float, float, HistoryPolicy::FIFO>>>
SignalStateDetectors;
std::vector<AgentHandle> SignalStateDetectorAgents;
for (auto SignalConfiguration : AppConfig.SignalConfigurations) {
//
// Create deluxe sensors.
//
Sensors.emplace_back(C->createSensor<float>(SignalConfiguration.Name));
//
// Create functionalities for SignalStateDetector.
//
SampleMatchesFunctions.emplace_back(new PartialFunction<float, float>(
{
{{-SignalConfiguration.OuterBound, -SignalConfiguration.InnerBound},
std::make_shared<LinearFunction<float, float>>(
-SignalConfiguration.OuterBound, 0.f,
-SignalConfiguration.InnerBound, 1.f)},
{{-SignalConfiguration.InnerBound, SignalConfiguration.InnerBound},
std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{SignalConfiguration.InnerBound, SignalConfiguration.OuterBound},
std::make_shared<LinearFunction<float, float>>(
SignalConfiguration.InnerBound, 1.f,
SignalConfiguration.OuterBound, 0.f)},
},
0));
SampleMismatchesFunctions.emplace_back(new PartialFunction<float, float>(
{
{{-SignalConfiguration.OuterBound, -SignalConfiguration.InnerBound},
std::make_shared<LinearFunction<float, float>>(
-SignalConfiguration.OuterBound, 1.f,
-SignalConfiguration.InnerBound, 0.f)},
{{-SignalConfiguration.InnerBound, SignalConfiguration.InnerBound},
std::make_shared<LinearFunction<float, float>>(0.f, 0.f)},
{{SignalConfiguration.InnerBound, SignalConfiguration.OuterBound},
std::make_shared<LinearFunction<float, float>>(
SignalConfiguration.InnerBound, 0.f,
SignalConfiguration.OuterBound, 1.f)},
},
1));
SignalIsStableFunctions.emplace_back(new PartialFunction<float, float>(
{
{{-SignalConfiguration.OuterBoundDrift,
-SignalConfiguration.InnerBoundDrift},
std::make_shared<LinearFunction<float, float>>(
-SignalConfiguration.OuterBoundDrift, 0.f,
-SignalConfiguration.InnerBoundDrift, 1.f)},
{{-SignalConfiguration.InnerBoundDrift,
SignalConfiguration.InnerBoundDrift},
std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{SignalConfiguration.InnerBoundDrift,
SignalConfiguration.OuterBoundDrift},
std::make_shared<LinearFunction<float, float>>(
SignalConfiguration.InnerBoundDrift, 1.f,
SignalConfiguration.OuterBoundDrift, 0.f)},
},
0));
SignalIsDriftingFunctions.emplace_back(new PartialFunction<float, float>(
{
{{-SignalConfiguration.OuterBoundDrift,
-SignalConfiguration.InnerBoundDrift},
std::make_shared<LinearFunction<float, float>>(
-SignalConfiguration.OuterBoundDrift, 1.f,
-SignalConfiguration.InnerBoundDrift, 0.f)},
{{-SignalConfiguration.InnerBoundDrift,
SignalConfiguration.InnerBoundDrift},
std::make_shared<LinearFunction<float, float>>(0.f, 0.f)},
{{SignalConfiguration.InnerBoundDrift,
SignalConfiguration.OuterBoundDrift},
std::make_shared<LinearFunction<float, float>>(
SignalConfiguration.InnerBoundDrift, 0.f,
SignalConfiguration.OuterBoundDrift, 1.f)},
},
1));
NumOfSamplesMatchFunctions.emplace_back(new StepFunction<float, float>(
1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepUp));
NumOfSamplesMismatchFunctions.emplace_back(new StepFunction<float, float>(
1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepDown));
//
// Create SignalStateDetector functionality
//
SignalStateDetectors.emplace_back(
new SignalStateDetector<float, float, float, HistoryPolicy::FIFO>(
SignalConfiguration.Output ? SignalProperties::OUTPUT
: SignalProperties::INPUT,
std::numeric_limits<int>::max(), SampleMatchesFunctions.back(),
SampleMismatchesFunctions.back(), NumOfSamplesMatchFunctions.back(),
NumOfSamplesMismatchFunctions.back(),
SignalIsDriftingFunctions.back(), SignalIsStableFunctions.back(),
SignalConfiguration.SampleHistorySize, SignalConfiguration.DABSize,
SignalConfiguration.DABHistorySize));
//
// Create low-level deluxe agents
//
SignalStateDetectorAgents.push_back(createSignalStateDetectorAgent(
C, SignalConfiguration.Name, SignalStateDetectors.back()));
//
// Connect sensors to low-level agents.
//
LOG_INFO("Connect sensors to their corresponding low-level agents.");
C->connectSensor(SignalStateDetectorAgents.back(), 0, Sensors.back(),
SignalConfiguration.Name);
C->connectAgents(SystemStateDetectorAgent, SignalStateDetectors.size() - 1,
SignalStateDetectorAgents.back(),
SignalConfiguration.Name);
}
//
// For simulation output, create a logger agent writing the output of the
// high-level agent into a CSV file.
//
LOG_INFO("Create a logger agent.");
// Create CSV writer.
std::ofstream OutputCSV(AppConfig.OutputFilePath);
// The agent writes each new input value into a CSV file and produces nothing.
using Input = std::pair<SystemStateTuple, bool>;
using Result = Optional<DeluxeTuple<unit_t>>;
using Handler = std::function<Result(Input)>;
std::string Name = "Logger Agent";
AgentHandle LoggerAgent =
C->createAgent("Logger Agent", Handler([&OutputCSV](Input I) -> Result {
OutputCSV << std::get<0>(I.first) << std::endl;
return Result();
}));
//
// Connect the high-level agent to the logger agent.
//
LOG_INFO("Connect the high-level agent to the logger agent.");
C->connectAgents(LoggerAgent, 0, SystemStateDetectorAgent,
"SystemStateDetector Channel");
//
// Do simulation.
//
LOG_INFO("Setting up and performing simulation.");
//
// Initialize deluxe context for simulation.
//
C->initializeSimulation();
//
// Open CSV files and register them for their corresponding sensors.
//
//
// Simulate.
//
C->simulate(AppConfig.NumberOfSimulationCycles);
return 0;
}
diff --git a/apps/ccam/statehandlerutils.h b/apps/ccam/statehandlerutils.h
index 477bafe..b19eac4 100644
--- a/apps/ccam/statehandlerutils.h
+++ b/apps/ccam/statehandlerutils.h
@@ -1,182 +1,179 @@
#ifndef STATEHANDLERUTILS_H
#define STATEHANDLERUTILS_H
#include "rosa/agent/Abstraction.hpp"
#include "rosa/agent/Confidence.hpp"
#include "rosa/agent/FunctionAbstractions.hpp"
#include <functional>
#include <iostream>
#include <tuple>
#include <vector>
#include "rosa/config/version.h"
#include "rosa/agent/SignalStateDetector.hpp"
#include "rosa/agent/SystemStateDetector.hpp"
#include "rosa/deluxe/DeluxeContext.hpp"
#include "rosa/support/csv/CSVReader.hpp"
#include "rosa/support/csv/CSVWriter.hpp"
#include <fstream>
#include <limits>
#include <memory>
#include <streambuf>
using namespace rosa;
using namespace rosa::agent;
using namespace rosa::deluxe;
using namespace rosa::terminal;
// Signal State
using SignalStateTuple = DeluxeTuple<float, uint32_t, uint8_t, float, uint8_t,
uint32_t, bool, bool, bool>;
AgentHandle createSignalStateDetectorAgent(
std::unique_ptr<DeluxeContext> &C, const std::string &Name,
std::shared_ptr<
SignalStateDetector<float, float, float, HistoryPolicy::FIFO>>
SigSD) {
using Input = std::pair<DeluxeTuple<float>, bool>;
using Result = Optional<SignalStateTuple>;
using Handler = std::function<Result(Input)>;
return C->createAgent(
Name, Handler([&Name, &SigSD](Input I) -> Result {
LOG_INFO_STREAM << "\n******\n"
<< Name << " " << (I.second ? "<New>" : "<Old>")
<< " value: " << std::get<0>(I.first) << "\n******\n";
auto StateInfo = SigSD->detectSignalState(std::get<0>(I.first));
if (I.second) {
SignalStateTuple Res = {
std::get<0>(I.first),
StateInfo.SignalStateID,
StateInfo.SignalProperty,
StateInfo.SignalStateConfidence,
StateInfo.SignalStateCondition,
StateInfo.NumberOfInsertedSamplesAfterEntrance,
StateInfo.SignalStateIsValid,
StateInfo.SignalStateJustGotValid,
StateInfo.SignalStateIsValidAfterReentrance};
return Result(Res);
}
return Result();
}));
}
// System State
using SystemStateTuple = DeluxeTuple<std::string, uint32_t, float, uint8_t,
uint32_t, bool, bool, bool, bool>;
template <std::size_t size, typename ret, typename functype, typename... A>
struct Handler_helper;
template <typename B, typename func, typename A, typename... As>
struct function_helper {
static_assert(std::conjunction_v<std::is_same<A, As>...>,
"All types need to be identical");
static B function(A valA, As... valAs) {
std::vector<A> ar({valA, valAs...});
return func()(ar);
}
};
template <typename ret, typename typeA, typename functype, typename... B>
struct Handler_helper<0, ret, functype, typeA, B...> {
using handler = function_helper<ret, functype, B...>;
};
template <std::size_t size, typename ret, typename typeA, typename functype,
typename... B>
struct Handler_helper<size, ret, functype, typeA, B...> {
using handler =
typename Handler_helper<size - 1, ret, functype, typeA,
std::pair<typeA, bool>, B...>::handler;
};
template <std::size_t size, typename ret, typename functype, typename typeA>
using Handler = typename Handler_helper<size, ret, functype, typeA>::handler;
// todo: state-detector durschleifen
template <typename ret, typename A> struct function {
ret operator()(A a) {
// std::vector<SystemStateInformation> out;
for (auto tmp1 : a) {
// convert tuple to info struct out.push_back({});
(void)tmp1;
LOG_INFO_STREAM << "new SignalStateTuple!\n";
}
// feed state detector
// return result
return ret();
}
};
using arr = std::vector<std::pair<SignalStateTuple, bool>>;
-auto HandlerFunction = Handler<4, Optional<SystemStateTuple>,
- function<Optional<SystemStateTuple>, arr>,
- SignalStateTuple>::function;
-
template <size_t NumOfSlaves>
AgentHandle createSystemStateDetectorAgent(
std::unique_ptr<DeluxeContext> &C, const std::string &Name,
- std::shared_ptr<PartialFunction<float, float>> BrokenDelayFunction,
- std::shared_ptr<PartialFunction<float, float>> OkDelayFunction) {
+ std::shared_ptr<PartialFunction<uint32_t, float>> BrokenDelayFunction,
+ std::shared_ptr<PartialFunction<uint32_t, float>> OkDelayFunction) {
LOG_TRACE("Creating fixed SystemStateDetectorAgent");
using Input = SignalStateTuple;
using Result = Optional<SystemStateTuple>;
auto HandlerFunction =
Handler<NumOfSlaves, Result, function<Optional<SystemStateTuple>, arr>,
Input>::function;
- std::shared_ptr<SystemStateDetector<float, float, float, HistoryPolicy::FIFO>>
- SysSD(new SystemStateDetector<float, float, float, HistoryPolicy::FIFO>(
+ std::shared_ptr<
+ SystemStateDetector<uint32_t, float, float, HistoryPolicy::FIFO>>
+ SysSD(new SystemStateDetector<uint32_t, float, float, HistoryPolicy::FIFO>(
std::numeric_limits<uint32_t>::max(), BrokenDelayFunction,
OkDelayFunction));
return C->createAgent(Name, std::function(HandlerFunction));
}
AgentHandle createSystemStateDetectorAgent(
std::unique_ptr<DeluxeContext> &C, const std::string &Name,
- uint8_t NumOfSlaves,
- std::shared_ptr<PartialFunction<float, float>> BrokenDelayFunction,
- std::shared_ptr<PartialFunction<float, float>> OkDelayFunction) {
+ size_t NumOfSlaves,
+ std::shared_ptr<PartialFunction<uint32_t, float>> BrokenDelayFunction,
+ std::shared_ptr<PartialFunction<uint32_t, float>> OkDelayFunction) {
LOG_TRACE("Creating dynamic SystemStateDetectorAgent");
switch (NumOfSlaves) {
- // clang-format off
+ // clang-format off
case 2: return createSystemStateDetectorAgent< 2>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 3: return createSystemStateDetectorAgent< 3>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 4: return createSystemStateDetectorAgent< 4>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 5: return createSystemStateDetectorAgent< 5>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 6: return createSystemStateDetectorAgent< 6>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 7: return createSystemStateDetectorAgent< 7>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 8: return createSystemStateDetectorAgent< 8>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 9: return createSystemStateDetectorAgent< 9>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 10: return createSystemStateDetectorAgent<10>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 11: return createSystemStateDetectorAgent<11>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 12: return createSystemStateDetectorAgent<12>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 13: return createSystemStateDetectorAgent<13>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 14: return createSystemStateDetectorAgent<14>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 15: return createSystemStateDetectorAgent<15>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 16: return createSystemStateDetectorAgent<16>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 17: return createSystemStateDetectorAgent<17>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 18: return createSystemStateDetectorAgent<18>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 19: return createSystemStateDetectorAgent<19>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 20: return createSystemStateDetectorAgent<20>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 21: return createSystemStateDetectorAgent<21>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 22: return createSystemStateDetectorAgent<22>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 23: return createSystemStateDetectorAgent<23>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 24: return createSystemStateDetectorAgent<24>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 25: return createSystemStateDetectorAgent<25>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 1:
default: return createSystemStateDetectorAgent<1>(C, Name, BrokenDelayFunction, OkDelayFunction);
// clang-format on
}
}
#endif // STATEHANDLERUTILS_H
diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp
index b2c43ce..20df4cf 100644
--- a/examples/CSVFiles/main.cpp
+++ b/examples/CSVFiles/main.cpp
@@ -1,352 +1,373 @@
//===-- examples/CSVFiles/main.cpp ------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \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.
+/// \c rosa::writer classes. Focus is on the tuple impementations.
///
//===----------------------------------------------------------------------===//
-#include <cstdint>
+#include <algorithm>
#include <cstddef>
+#include <cstdint>
+#include <fstream>
#include <iostream>
#include <istream>
-#include <sstream>
-#include <fstream>
+#include <map>
#include <ostream>
+#include <sstream>
#include <string>
-#include <vector>
-#include <typeinfo>
-#include <map>
-#include <algorithm>
#include <tuple>
#include <type_traits>
-#include <unistd.h>
+#include <typeinfo>
+#include <vector>
+//#include <unistd.h>
-//includes for an complete example to read and write
-//with sensors and agents.
+// 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.
+// 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 csvPath = "../examples/CSVFiles/";
const std::string csvFileWithHeader = csvPath + "HR-New.csv";
-const std::string csvFileNoHeader = csvPath + "HR.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";
+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.
+/// 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<int, std::string, std::string, int, int> it(file_header_data);
-
- it.setDelimeter(',');
-
- it++;
- 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++;
-
- //-------------------------------------------------------------------
- // a possiblity to get the data out of the iterator
- std::tuple<int, std::string, std::string, int, int> 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<double, std::vector<int>> it2(file, 1);
-
- //try to skip a valid number of lines after the header
- csv::CSVIterator<double, float, int, int, float> 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<double, float, int, int, float> 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<double, float, int, int, float> 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<double, float, int, int, float> it2_3(file2, 1, csv::HeaderInformation::HasNoHeader);
-
- //try to skip a valid number of lines after the header, but with different delimeter
- csv::CSVIterator<double, float, int, int, float> it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';');
-
- // if you skip more lines than valid, you generate an infinte loop
- //csv::CSVIterator<double, float, int, int, float> 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<double, float, int, float> it4(file_header_data_6);
+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<int, std::string, std::string, int, int> it(
+ file_header_data);
+
+ it.setDelimeter(',');
+
+ it++;
+ 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++;
+
+ //-------------------------------------------------------------------
+ // a possiblity to get the data out of the iterator
+ std::tuple<int, std::string, std::string, int, int> 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<double, std::vector<int>> it2(file, 1);
+
+ // try to skip a valid number of lines after the header
+ csv::CSVIterator<double, float, int, int, float> 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<double, float, int, int, float> 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<double, float, int, int, float> 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<double, float, int, int, float> it2_3(
+ file2, 1, csv::HeaderInformation::HasNoHeader);
+
+ // try to skip a valid number of lines after the header, but with different
+ // delimeter
+ csv::CSVIterator<double, float, int, int, float> it2_4(
+ file3, 2, csv::HeaderInformation::HasHeader, ';');
+
+ // if you skip more lines than valid, you generate an infinte loop
+ // csv::CSVIterator<double, float, int, int, float> 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<double, float, int, float> it4(file_header_data_6);
}
///
-/// This function tests the basic CSVTupleWriter capablities, and shows you
-/// how you could work with this class.
+/// 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<int, float, std::string> wri(file_header_out);
-
- //
- // Create test tuples
- //
- std::tuple<int, float, std::string> values(5, 8.3, "hallo");
- std::tuple<int, float, std::string> values2(3, 8.3, "end");
-
- //
- // Create test header lines for the test tuples
- //
- std::array<std::string, 3> header {"zero column", "first column", "second column"};
-
- std::array<std::string, 4> headerWrong {"zero column", "first column", "second column", "third column"};
-
- std::array<std::string, 2> 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);
-
+void testtupleCSVWriter(void) {
+ //
+ // Create output writer with an file
+ //
+ std::ofstream file_header_out(csvFileWriteHea);
+ csv::CSVTupleWriter<int, float, std::string> wri(file_header_out);
+
+ //
+ // Create test tuples
+ //
+ std::tuple<int, float, std::string> values(5, 8.3f, "hallo");
+ std::tuple<int, float, std::string> values2(3, 8.3f, "end");
+
+ //
+ // Create test header lines for the test tuples
+ //
+ std::array<std::string, 3> header{"zero column", "first column",
+ "second column"};
+
+ std::array<std::string, 4> headerWrong{"zero column", "first column",
+ "second column", "third column"};
+
+ std::array<std::string, 2> 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.
+/// 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<rosa::deluxe::DeluxeContext> 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<int>("Element1 Sensor");
- AgentHandle Elem1Sensor = C->createSensor<float>("Element2 Sensor");
- AgentHandle Elem2Sensor = C->createSensor<std::string>("Element3 Sensor");
-
-
- //
- // Initialize deluxe context for simulation.
- //
- C->initializeSimulation();
-
- // Type aliases for iterators
- using Iterator = rosa::csv::CSVIterator<int, float, std::string>;
- using IteratorValue = std::tuple<int, float, std::string>;
-
- static_assert (std::is_same<typename Iterator::value_type, IteratorValue>::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);
+void testsplitTupleIterator(void) {
+ //
+ // Create deluxe context
+ //
+ std::unique_ptr<rosa::deluxe::DeluxeContext> 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<int>("Element1 Sensor");
+ AgentHandle Elem1Sensor = C->createSensor<float>("Element2 Sensor");
+ AgentHandle Elem2Sensor = C->createSensor<std::string>("Element3 Sensor");
+ //
+ // Initialize deluxe context for simulation.
+ //
+ C->initializeSimulation();
+
+ // Type aliases for iterators
+ using Iterator = rosa::csv::CSVIterator<int, float, std::string>;
+ using IteratorValue = std::tuple<int, float, std::string>;
+
+ static_assert(
+ std::is_same<typename Iterator::value_type, IteratorValue>::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.
+/// 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<int, float, std::string> 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<int, float, std::string>(1000, 50.6, "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.004);
-
- // 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.4);
- T0Writer << (3);
- T2Writer << "again";
- T0Writer << (4);
- T1Writer << (-0.1);
- T1Writer << (-0.2);
- T2Writer << "and";
- T1Writer << (-0.3);
- T2Writer << "splitting";
- T2Writer << "once again";
-
- // again writing data of one tuple entry to the different writers in a random fashion.
- T1Writer << (-0.004);
- T2Writer << "splitting is cool";
- T0Writer << (-30);
-
+// NOTE (Maxi): I had to add an "f" to some numbers to declare them as float,
+// otherwise: "C:\Users\maxgot\Source\SoC_RoSA\examples\CSVFiles\main.cpp:314:
+// warning: C4305: 'argument': truncation from 'double' to 'const float'"
+void testsplitTupleWriter(void) {
+ //
+ // Create output writer with an file
+ //
+ std::ofstream file_header_out(csvFileWriteNoHeaSplit);
+ csv::CSVTupleWriter<int, float, std::string> 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<int, float, std::string>(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){
+int main(void) {
LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red
- << ExampleName << " example" << terminal::Color::Default << '\n';
+ << 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.");
-
+ LOG_INFO("All tests finished.");
return 0;
}
-
diff --git a/include/rosa/agent/SystemState.hpp b/include/rosa/agent/SystemState.hpp
index e1fa090..d1b8920 100644
--- a/include/rosa/agent/SystemState.hpp
+++ b/include/rosa/agent/SystemState.hpp
@@ -1,156 +1,173 @@
//===-- rosa/agent/SystemState.hpp ------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SystemState.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *system state* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_SYSTEMSTATE_HPP
#define ROSA_AGENT_SYSTEMSTATE_HPP
#include "rosa/agent/Functionality.h"
#include "rosa/agent/SignalState.hpp"
#include "rosa/support/debug.hpp"
#include <vector>
namespace rosa {
namespace agent {
// System state conditions defining how the condition of a \c
/// rosa::agent::SystemState is saved in \c rosa::agent::SystemStateInformation.
enum class SystemStateCondition : uint8_t {
STABLE = 0, ///< The system state is stable
DRIFTING = 1, ///< The system state is drifting
MALFUNCTIONING = 2, ///< The system state is malfunctioning
UNKNOWN = 3 ///< The system state is unknown
};
/// TODO: write description
template <typename CONFDATATYPE> struct SystemStateInformation {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
"confidence type is not to arithmetic");
/// The system state ID saved as an uint32_teger number
uint32_t SystemStateID;
/// The SystemStateConfidence shows the overall confidence value of the system
/// state.
CONFDATATYPE OverallDetectionConfidence;
/// The SystemStateCondition shows the condition of a system state (stable,
/// drifting, malfunctioning, or unknown)
SystemStateCondition SystemStateCondition;
/// The SystemStateIsValid saves the number of samples which have been
/// inserted into the state after entering it.
uint32_t NumberOfInsertedSamplesAfterEntrance;
/// The SystemStateIsValid shows whether a state is valid or invalid.
/// In this context, valid means that enough samples which are in close
/// proximitry have been inserted into the state.
bool SystemStateIsValid;
/// The SystemStateJustGotValid shows whether a system state got valid
/// (toggled from invalid to valid) during the current inserted sample.
bool SystemStateJustGotValid;
/// The SystemStateIsValidAfterReentrance shows whether a system state is
/// valid after the variable changed back to it again.
bool SystemStateIsValidAfterReentrance;
};
// todo: do we need PROCDATATYPE?
/// TODO TEXT
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE>
class SystemState : public Functionality {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT(std::is_arithmetic<INDATATYPE>::value,
"input data type is not to arithmetic");
STATIC_ASSERT(std::is_arithmetic<CONFDATATYPE>::value,
"confidence abstraction type is not to arithmetic");
STATIC_ASSERT(std::is_arithmetic<PROCDATATYPE>::value,
"process data type is not to arithmetic");
private:
SystemStateInformation<CONFDATATYPE> SystemStateInfo;
std::vector<SignalStateInformation<CONFDATATYPE>> Signals;
uint32_t NumberOfSignals;
public:
- /// TODO write
- SystemState(uint32_t SignalStateID, uint32_t NumberOfSignals) noexcept
- : SystemStateInfo{SignalStateID,
- 0,
- SystemStateCondition::UNKNOWN,
- 0,
- false,
- false,
- false},
+ /// TODO: write description
+ SystemState(uint32_t StateID, uint32_t NumberOfSignals) noexcept
+ : SystemStateInfo{StateID, 0, SystemStateCondition::UNKNOWN, 0, false,
+ false, false},
NumberOfSignals(NumberOfSignals) {
+ //@benedikt: there is now possibility to not doing the resize within these
+ //{}-brackets, right?
Signals.resize(NumberOfSignals);
}
/// Destroys \p this object.
~SystemState(void) = default;
+ /// TODO: write description
template <std::size_t size>
- void
- insertSignalStateInfos(const std::array<SystemStateInformation<CONFDATATYPE>,
- size> &Data) noexcept {
+ void insertSignalStateInformation(
+ const std::array<SystemStateInformation<CONFDATATYPE>, size>
+ &Data) noexcept {
+ //@Daniel: is it possible to check with a ASSERT/STATIC_ASSERT whether the
+ // number of parameter is the same as NumberOfSignals (which is the length
+ // of the vectror)?
ASSERT(size <= NumberOfSignals);
std::size_t counter = 0;
for (auto tmp : Data) {
Signals.at(counter) = tmp;
counter++;
}
}
+ /// TODO: write description
template <typename... Types>
std::enable_if_t<std::conjunction_v<std::is_same<
Types, SystemStateInformation<CONFDATATYPE>>...>,
void>
- insertSignalStateInfos(Types... Data) {
+ insertSignalStateInformation(Types... Data) {
+ //@Daniel: is it possible to check with a ASSERT/STATIC_ASSERT whether the
+ // number of parameter is the same as NumberOfSignals (which is the length
+ // of the vectror)?
+ //
+ // TODO (future): think about saving the state information in a history
insertSignalStateInfos(
std::array<SystemStateInformation<CONFDATATYPE>, sizeof...(Data)>(
{Data...}));
}
// returns true if they are identical
+ /// TODO: write description
template <std::size_t size>
- bool
- compareSignalStateInfos(const std::array<SystemStateInformation<CONFDATATYPE>,
- size> &Data) noexcept {
+ bool compareSignalStateInformation(
+ const std::array<SystemStateInformation<CONFDATATYPE>, size>
+ &Data) noexcept {
+ //@Daniel: is it possible to check with a ASSERT/STATIC_ASSERT whether the
+ // number of parameter is the same as NumberOfSignals (which is the length
+ // of the vectror)?
+ //
+ // TODO (future): think about saving the state information in a history
std::size_t counter = 0;
for (auto tmp : Data) {
if (Signals.at(counter) != tmp)
return false;
counter++;
}
return true;
}
// checks only the given amount
+ /// TODO: write description
template <typename... Types>
std::enable_if_t<std::conjunction_v<std::is_same<
Types, SystemStateInformation<CONFDATATYPE>>...>,
bool>
- compareSignalStateInfos(Types... Data) {
+ compareSignalStateInformation(Types... Data) {
+ //@Daniel: is it possible to check with a ASSERT/STATIC_ASSERT whether the
+ // number of parameter is the same as NumberOfSignals (which is the length
+ // of the vectror)?
return compareSignalStateInfos(
std::array<SystemStateInformation<CONFDATATYPE>, sizeof...(Data)>(
{Data...}));
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SYSTEMSTATE_HPP
diff --git a/include/rosa/agent/SystemStateDetector.hpp b/include/rosa/agent/SystemStateDetector.hpp
index d18607c..b3fc125 100644
--- a/include/rosa/agent/SystemStateDetector.hpp
+++ b/include/rosa/agent/SystemStateDetector.hpp
@@ -1,104 +1,114 @@
//===-- rosa/agent/SystemStateDetector.hpp ----------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SystemStateDetector.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *system state detector* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_SYSTEMSTATEDETECTOR_HPP
#define ROSA_AGENT_SYSTEMSTATEDETECTOR_HPP
#include "rosa/agent/Functionality.h"
#include "rosa/agent/StateDetector.hpp"
#include "rosa/agent/SystemState.hpp"
#include "rosa/support/debug.hpp"
namespace rosa {
namespace agent {
// todo: löschen , std::size_t NUMOFINPUTSIGNALS, std::size_t NUMOFOUTPUTSIGNALS
/// TODO: write description
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE,
HistoryPolicy HP>
class SystemStateDetector
: public StateDetector<INDATATYPE, CONFDATATYPE, PROCDATATYPE, HP> {
- //@maxi added them to make it compilable is this what you wanted?
using StateDetector =
StateDetector<INDATATYPE, CONFDATATYPE, PROCDATATYPE, HP>;
using PartFuncPointer = typename StateDetector::PartFuncPointer;
private:
// For the convinience to write a shorter data type name
using SystemStatePtr =
std::shared_ptr<SystemState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>>;
- /// The NextSystemStateID is a counter variable which stores the ID which
- /// the
- /// next system state shall have.
- uint32_t NextSystemStateID;
-
- /// The SystemStateHasChanged is a flag that show whether the observed
- /// system
- /// has changed its state.
- bool SystemStateHasChanged;
-
/// The CurrentSystemState is a pointer to the (saved) system state in which
/// the actual state of the observed system is.
SystemStatePtr CurrentSystemState;
/// The DetectedSystemStates is a history in that all detected system states
/// are saved.
DynamicLengthHistory<SystemStatePtr, HP> DetectedSystemStates;
/// The FuzzyFunctionDelayTimeToGetBroken is the fuzzy function that gives
/// the confidence whether the system is Broken because of an input change
/// without an output change or vice versa. A small time gap between the two
/// shall be allowed.
PartFuncPointer FuzzyFunctionDelayTimeToGetBroken;
/// The FuzzyFunctionDelayTimeToBeWorking is the fuzzy function that gives
/// the
/// confidence whether the system is still OK allthough an input change
/// without an output change or vice versa.
PartFuncPointer FuzzyFunctionDelayTimeToBeWorking;
public:
// todo zwei parameter für variablen anzahl
/// TODO: write description
SystemStateDetector(
uint32_t MaximumNumberOfSystemStates,
PartFuncPointer FuzzyFunctionDelayTimeToGetBroken,
PartFuncPointer FuzzyFunctionDelayTimeToBeWorking) noexcept
- : NextSystemStateID(1), SystemStateHasChanged(false),
- CurrentSystemState(nullptr),
+ : CurrentSystemState(nullptr),
DetectedSystemStates(MaximumNumberOfSystemStates),
FuzzyFunctionDelayTimeToGetBroken(FuzzyFunctionDelayTimeToGetBroken),
- FuzzyFunctionDelayTimeToBeWorking(FuzzyFunctionDelayTimeToBeWorking) {}
+ FuzzyFunctionDelayTimeToBeWorking(FuzzyFunctionDelayTimeToBeWorking) {
+ //@Benedikt: if I write "NextStateID(1), StateHasChanged(false)" before the
+ //{}-brackets, the compiler tells me: "SystemStateDetector.hpp:72:9: error:
+ // member initializer 'NextStateID'/'StateHasChanged' does not name a
+ // non-static data member or base class"
+ this->NextStateID = 1;
+ this->StateHasChanged = false;
+ }
/// Destroys \p this object.
~SystemStateDetector(void) = default;
/// TODO: write description
SystemStateInformation<CONFDATATYPE>
detectSystemState(INDATATYPE Sample) noexcept {
// dummy line
Sample = 1;
}
+
+private:
+ /// Creates a new signal state and adds it to the signal state vector in which
+ /// all known states are saved.
+ ///
+ /// \return a pointer to the newly created signal state or NULL if no state
+ /// could be created.
+ SystemStatePtr createNewSignalState(void) noexcept {
+ SystemStatePtr S(new SignalState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>(
+ this->NextStateID, this->NumberOfSignals));
+
+ DetectedSystemStates.addEntry(S);
+
+ return S;
+ }
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SYSTEMSTATEDETECTOR_HPP
diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp
index fa328a2..c523494 100644
--- a/include/rosa/deluxe/DeluxeAgent.hpp
+++ b/include/rosa/deluxe/DeluxeAgent.hpp
@@ -1,1456 +1,1457 @@
//===-- 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 <map>
/// 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 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<DeluxeExecutionPolicy> 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<Token> 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<token_size_t> 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<bool> InputChanged;
/// 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 \c rosa::AbstractTokenizedStorage in the \c
/// std::vector indicates which argument of \p this object's processing
/// function the tuple is; and the position of the value in the \c
/// rosa::AbstractTokenizedStorage indicates which element of that tuple the
/// value is. See also \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<std::unique_ptr<AbstractTokenizedStorage>> 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<AbstractTokenizedStorage> 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<Token> 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<void(void)>;
/// 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
+ /// 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<AgentHandle> 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<Optional<AgentHandle>> Slaves;
/// Associates \c rosa::id_t values to corresponding indices of registered
/// *slaves*.
///
/// \see \c rosa::deluxe::DeluxeAgent::Slaves
std::map<id_t, size_t> 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 <typename As> 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 <typename Ts> 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<Ts...>::TT == InputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> prepareInputValueAtPos(TypeList<Ts...>, Seq<S0...>) 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<TypeList<As...>>::Value &&
/// sizeof...(As) == sizeof...(S0)
/// \endcode Dynamically, type arguments \p As... match the input types of \p
/// this object: \code
/// inputTypesMatch<TypeList<As...>>()
/// \endcode
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...> prepareCurrentInputs(Seq<S0...>) 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 <typename T, typename... Ts, typename... As, size_t... S0>
static std::tuple<Optional<T>, Optional<Ts>...>
invokeWithTuple(std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args,
Seq<S0...>) 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<Ts...>::TT == MasterOutputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts>
void
handleMasterOutputAtPos(const Optional<DeluxeTuple<Ts...>> &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<Offset, TypeList<Ts...>>::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<typename TypeListDrop<Offset,
/// TypeList<Ts...>>::Type>() &&
/// sizeof...(S0) == NumberOfMasterOutputs
/// \endcode
template <size_t Offset, typename... Ts, size_t... S0>
void handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) 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<TypeList<T, Ts..., As...>>::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<MTs...>::TT && OutputType == T::TT &&
/// inputTypesMatch<TypeList<As...>>() &&
/// masterOutputTypesMatch<TypeList<Ts...>>()
/// \endcode
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
H triggerHandlerFromProcessingFunctions(
std::function<std::tuple<Optional<Ts>...>(
std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) 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<TypeList<MT, T, Ts..., As...>::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 <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::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::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&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<DeluxeExecutionPolicy> &&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<AgentHandle> 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<AgentHandle> _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<AgentHandle> 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<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).MasterInputType
/// == MasterOutputTypes[Pos])) ||
/// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind &&
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).MasterInputType ==
/// MasterOutputTypes[Pos])))
/// \endcode
void registerSlave(const size_t Pos,
const Optional<AgentHandle> 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<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToMaster(const DeluxeTuple<Ts...> &Value, Seq<S0...>) 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<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) 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<T>::Value
/// \endcode
template <typename T>
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<T>::Value
/// \endcode
template <typename T>
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 {
/// Creates storages for data of types \p Ts... in a \c std::vector of \c
/// rosa::TokenizedStorage.
///
/// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c
/// rosa::deluxe::DeluxeAgent::InputValues. That is due to not being able to use
/// an initializer list directly; the initializer list always copies but \c
/// std::unique_ptr is not copyable.
///
/// \tparam Ts types to create storages for
///
/// \note Instantiation fails if any of the type arguments \p Ts... is not an
/// instance of \c rosa::deluxe::DeluxeTuple.
///
/// \return \c std::vector with pointers for the created storage objects
///
/// \pre Statically, all the type arguments \p Ts... are instances of \c
/// rosa::deluxe::DeluxeTuple: \code
/// TypeListAllDeluxeTuple<TypeList<Ts...>>::Value
/// \endcode
template <typename... Ts>
std::vector<std::unique_ptr<AbstractTokenizedStorage>>
makeInputStorages(void) noexcept {
std::vector<std::unique_ptr<AbstractTokenizedStorage>> InputStorages;
(InputStorages.push_back(
std::make_unique<typename TokenizedStorageForTypeList<
typename UnwrapDeluxeTuple<Ts>::Type>::Type>()),
...);
return InputStorages;
}
/// Template \c struct whose specializations provide a recursive implementation
/// for \c TypesMatchList.
///
/// \tparam As types to match
template <typename... As> 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 <typename... Ts, typename... As>
struct TypesMatchImpl<DeluxeTuple<Ts...>, As...> {
/// Tells whether types \c rosa::deluxe::DeluxeTuple<Ts...> 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<Ts...> 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<Ts...> and \p As... match \c
/// rosa::Token values stored in \p Tokens starting at position \p Pos
static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
return Pos < Tokens.size() && TypeToken<Ts...>::Value == Tokens[Pos] &&
TypesMatchImpl<As...>::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 <typename T, typename... As>
-struct TypesMatchImpl<T, As...> {
+template <typename T, typename... As> struct TypesMatchImpl<T, As...> {
/// 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<Token> &, 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<Token> &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<List>::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 <typename List> struct TypesMatchList;
/// Template specialization implementing the feature.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchList<TypeList<As...>> {
/// 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<Token> &Tokens) noexcept {
return TypesMatchImpl<As...>::f(Tokens, 0);
}
};
} // End namespace
-template <typename As>
-bool DeluxeAgent::inputTypesMatch(void) const noexcept {
+template <typename As> bool DeluxeAgent::inputTypesMatch(void) const noexcept {
return TypesMatchList<As>::f(InputTypes);
}
template <typename Ts>
bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept {
return TypesMatchList<Ts>::f(MasterOutputTypes);
}
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> DeluxeAgent::prepareInputValueAtPos(TypeList<Ts...>,
Seq<S0...>) const
noexcept {
using T = DeluxeTuple<Ts...>;
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && Pos < NumberOfInputs && T::TT == InputTypes[Pos]);
// The below should hold because of the above, just leave it for sanity check.
- ASSERT((true && ... &&
- (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ STATIC_ASSERT((true && ... &&
+ (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)),
+ "Should not happen");
const auto &SlaveInput = InputValues[Pos];
// Get all elements of the tuple in a fold expression.
return T(*static_cast<const Ts *>(
SlaveInput->pointerTo(static_cast<token_size_t>(S0)))...);
}
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...>
DeluxeAgent::prepareCurrentInputs(Seq<S0...>) const noexcept {
STATIC_ASSERT(TypeListAllDeluxeTuple<TypeList<As...>>::Value,
"not tuple types");
STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && inputTypesMatch<TypeList<As...>>());
return std::make_tuple(std::make_pair(
prepareInputValueAtPos<S0>(typename UnwrapDeluxeTuple<As>::Type(),
seq_t<As::Length>()),
InputChanged[S0])...);
}
template <typename T, typename... Ts, typename... As, size_t... S0>
std::tuple<Optional<T>, Optional<Ts>...> DeluxeAgent::invokeWithTuple(
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args, Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(As) == sizeof...(S0),
"wrong number of type parameters");
return F(std::get<S0>(Args)...);
}
template <size_t Pos, typename... Ts>
void DeluxeAgent::handleMasterOutputAtPos(
const Optional<DeluxeTuple<Ts...>> &Value) noexcept {
using MOT = DeluxeTuple<Ts...>;
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<MOT, EmptyDeluxeTuple>::value) {
- if (Value) {
- sendToSlave(Pos, *Value, seq_t<MOT::Length>());
+ if
+ constexpr(!std::is_same<MOT, EmptyDeluxeTuple>::value) {
+ if (Value) {
+ sendToSlave(Pos, *Value, seq_t<MOT::Length>());
+ }
}
- } else {
+ else {
(void)Value;
}
ASSERT(inv());
}
template <size_t Offset, typename... Ts, size_t... S0>
void DeluxeAgent::handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept {
using MOTs = typename TypeListDrop<Offset, TypeList<Ts...>>::Type;
STATIC_ASSERT(TypeListAllDeluxeTuple<MOTs>::Value,
"not tuple type arguments");
STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0),
"inconsistent arguments");
ASSERT(inv() && masterOutputTypesMatch<MOTs>() &&
sizeof...(S0) == NumberOfMasterOutputs);
// Handle each master-output position in a fold expression.
(handleMasterOutputAtPos<S0>(std::get<Offset + S0>(Output)), ...);
ASSERT(inv());
}
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions(
std::function<
std::tuple<Optional<Ts>...>(std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) noexcept {
using MT = DeluxeTuple<MTs...>;
STATIC_ASSERT((TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value),
"not tuple type arguments");
STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments");
ASSERT(MasterInputType == MT::TT && OutputType == T::TT &&
inputTypesMatch<TypeList<As...>>() &&
masterOutputTypesMatch<TypeList<Ts...>>());
return [ this, MF, F ]() noexcept {
// \note These indices work for both inputs and master-outputs.
using SlaveIndices = seq_t<sizeof...(As)>;
// Handle master-input.
// Do not do anything for master-input type \c
// rosa::deluxe::EmptyDeluxeTuple.
if (!std::is_same<MT, EmptyDeluxeTuple>::value) {
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles master-input."
<< std::endl;
// The assert must hold if \p this object was successfully constructed.
STATIC_ASSERT((true && ... && (static_cast<size_t>(
static_cast<token_size_t>(S0)) == S0)),
"Unexpected error");
const auto MasterInputArg = std::make_pair(
// Get all elements of the tuple in a fold expression.
MT(*static_cast<const MTs *>(
MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
MasterInputChanged);
MasterInputChanged = false;
const std::tuple<Optional<Ts>...> MasterOutput = MF(MasterInputArg);
handleMasterOutputs<0>(MasterOutput, SlaveIndices());
}
// Handle inputs.
// 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<As...>(SlaveIndices());
std::fill(InputChanged.begin(), InputChanged.end(), false);
const std::tuple<Optional<T>, Optional<Ts>...> Output =
invokeWithTuple(F, InputArgs, SlaveIndices());
const auto OutputToMaster = std::get<0>(Output);
if (OutputToMaster) {
sendToMaster(*OutputToMaster, seq_t<T::Length>());
}
handleMasterOutputs<1>(Output, SlaveIndices());
} else {
LOG_TRACE_STREAM << "DeluxeAgent " << Name << " skips input."
<< std::endl;
}
};
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
DeluxeAgent::DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&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)),
ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT),
NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT),
NumberOfMasterOutputs(NumberOfInputs), InputTypes({As::TT...}),
InputNextPos(NumberOfInputs, 0), InputChanged(NumberOfInputs, false),
InputValues(makeInputStorages<As...>()), MasterInputNextPos(0),
MasterInputChanged(false),
MasterInputValue(new typename TokenizedStorageForTypeList<
typename UnwrapDeluxeTuple<MT>::Type>::Type()),
MasterOutputTypes({Ts::TT...}),
FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F),
seq_t<MT::Length>())),
Slaves(NumberOfInputs) {
ASSERT(Kind == atoms::AgentKind);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl;
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToMaster(const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && OutputType == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfully constructed.
STATIC_ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(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<token_size_t, sizeof...(S0)> Indices{{S0...}};
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") sends to master ("
<< static_cast<bool>(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<S0>(Value))),
...);
}
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MasterOutputTypes[Pos] == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfully constructed.
STATIC_ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(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<token_size_t, sizeof...(S0)> 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<bool>(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<S0>(Value))),
...);
}
}
template <typename T>
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<T>::Value);
const size_t SlavePos = SlaveIds.at(Id);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from slave at position " << SlavePos
<< ": (" << static_cast<size_t>(Pos) << ") " << Value
<< std::endl;
// Save value.
*static_cast<T *>(InputValues[SlavePos]->pointerTo(Pos)) = Value;
// Update position of next value.
if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) {
InputNextPos[SlavePos] = 0;
}
// Set flag.
InputChanged[SlavePos] = true;
ASSERT(inv());
}
template <typename T>
void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept {
ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos &&
typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from master: (" << static_cast<size_t>(Pos)
<< ") " << Value << std::endl;
// Save value.
*static_cast<T *>(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/support/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp
index 5402018..c5678ef 100755
--- a/include/rosa/support/csv/CSVReader.hpp
+++ b/include/rosa/support/csv/CSVReader.hpp
@@ -1,486 +1,484 @@
//===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/csv/CSVReader.hpp
///
-/// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger (edwin.willegger@tuwien.ac.at)
+/// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger
+/// (edwin.willegger@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Facitilities to read CSV files.
///
/// \note The implementation is based on the solution at
/// https://stackoverflow.com/a/1120224
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_CSV_CSVREADER_HPP
#define ROSA_SUPPORT_CSV_CSVREADER_HPP
#include "rosa/support/debug.hpp"
#include "rosa/support/sequence.hpp"
+#include <algorithm>
#include <istream>
-#include <sstream>
-#include <vector>
#include <map>
-#include <algorithm>
#include <set>
+#include <sstream>
+#include <vector>
namespace rosa {
namespace csv {
/// Indicating it the CSV file contains any header or not
-enum class HeaderInformation {
- HasHeader,
- HasNoHeader
-};
+enum class HeaderInformation { HasHeader, HasNoHeader };
/// Anonymous namespace providing implementation details for
/// \c rosa::csv::CSVIterator, consider it private.
namespace {
/// Provides facility for parsing one value from a string.
///
/// \tparam T type of value to parse
/// \tparam IsSignedInt if \p T is a signed integral type, always use default
/// \tparam IsUnsignedInt if \p T is an unsigned integral type, always use
/// default
/// \tparam IsFloat if \p T is a floating-point type, always use default
/// \tparam IsString if \p T is \c std::string, always use default
///
/// \note Specializations of this struct are provided for arithmentic types
/// and \c std::string.
template <typename T,
bool IsSignedInt =
(std::is_integral<T>::value && std::is_signed<T>::value),
bool IsUnsignedInt =
(std::is_integral<T>::value && std::is_unsigned<T>::value),
bool IsFloat = std::is_floating_point<T>::value,
bool IsString = std::is_same<T, std::string>::value>
struct ValueParser {
///
///
/// \param Cell the \c std::string to parse
///
/// \return the parsed value
///
/// \note The function silently fails if cannot parse \p Cell for type \p T.
static T parse(const std::string &Cell) noexcept;
};
-template <typename T>
-struct ValueParser<T, true, false, false, false> {
+template <typename T> struct ValueParser<T, true, false, false, false> {
STATIC_ASSERT((std::is_integral<T>::value && std::is_signed<T>::value),
"wrong type"); // Sanity check.
static T parse(const std::string &Cell) noexcept {
return static_cast<T>(std::stoll(Cell));
}
};
-template <typename T>
-struct ValueParser<T, false, true, false, false> {
+template <typename T> struct ValueParser<T, false, true, false, false> {
STATIC_ASSERT((std::is_integral<T>::value && std::is_unsigned<T>::value),
"wrong type"); // Sanity check.
static T parse(const std::string &Cell) noexcept {
return static_cast<T>(std::stoull(Cell));
}
};
-template <typename T>
-struct ValueParser<T, false, false, true, false> {
+template <typename T> struct ValueParser<T, false, false, true, false> {
STATIC_ASSERT((std::is_floating_point<T>::value),
"wrong type"); // Sanity check.
static T parse(const std::string &Cell) noexcept {
return static_cast<T>(std::stold(Cell));
}
};
-template <typename T>
-struct ValueParser<T, false, false, false, true> {
+template <typename T> struct ValueParser<T, false, false, false, true> {
STATIC_ASSERT((std::is_same<T, std::string>::value),
"wrong type"); // Sanity check.
static T parse(const std::string &Cell) noexcept { return Cell; }
};
/// Parses and stores entries from a row of CSV data.
///
/// \tparam Ts types of values to parse and store, i.e. entries in the row
///
/// \note The implementation relies on \c rosa::csv::CSVRowParser, which is
/// implemented only for `arithmetic` types -- signed and unsigned integral
/// and floating-point types -- and for \c std::string. Those are the valid
/// values for \p Ts.
template <typename... Ts> class CSVRow {
private:
/// Parses a given row of CSV data into \c CSVRow::Data.
///
/// \ CSVRow::Data is filled with values parsed from \p LineStream. Entries
/// in the line are to be separated by commas, the character `,`.
///
/// \note Parsed values are silently converted to types \p Ts.
///
/// \note Parsing silently fails if values do not match \p Ts.
///
/// \tparam S0 indices to access tuple elements.
///
/// \param [in,out] LineStream the line to parse
///
/// \note The last argument is used only to get \p S0, the actual value of
/// the parameter is ignored.
template <size_t... S0>
void parseRow(std::stringstream &LineStream, char Delimeter, Seq<S0...>) {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0),
"Not matching template arguments.");
std::string Cell;
// Get fields and parse the values into the proper element of the tuple
// one by one in a fold expression.
((std::getline(LineStream, Cell, Delimeter),
std::get<S0>(Data) = ValueParser<Ts>::parse(Cell)),
...);
-
}
public:
-
/// Constructor with all possible parameters
///
- /// The function creates an instance of an CSVRow object and sets the attributes of the
+ /// The function creates an instance of an CSVRow object and sets the
+ /// attributes of the
/// object to the values of the parameters.
///
- /// \param SkipRows the number of data rows to skip, not taking header into account.
+ /// \param SkipRows the number of data rows to skip, not taking header into
+ /// account.
/// \param HeaderInfo is the first line of the file a header row or not.
/// \param Delimeter to seperate between the data entries within one row.
CSVRow(const size_t SkipRows = 0,
const HeaderInformation HeaderInfo = HeaderInformation::HasHeader,
- const char Delimeter = ',') :
- SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter),
- RowNumber(0), IsHeaderRead(false) {
- }
-
-
+ const char Delimeter = ',')
+ : SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter),
+ RowNumber(0), IsHeaderRead(false) {}
/// Parses and stores one row of CSV data.
///
/// The function reads one line from \p Str and parses it into
/// \c rosa::csv::CSVRow::Data using \c rosa::csv::CSVRowParser.
///
/// \param [in,out] Str input stream of a CSV file
void readNextRow(std::istream &Str) noexcept {
std::string Line;
std::getline(Str, Line);
- if(Line.size() > 0){
- std::stringstream LineStream(Line);
- parseRow(LineStream, Delimeter, seq_t<sizeof...(Ts)>());
+ if (Line.size() > 0) {
+ std::stringstream LineStream(Line);
+ parseRow(LineStream, Delimeter, seq_t<sizeof...(Ts)>());
- RowNumber = RowNumber + 1;
+ RowNumber = RowNumber + 1;
}
-
}
/// Read header row and stores it as \p std::string.
///
/// The function reads the first line of the csv file and stores the entries
/// in a vector.
///
/// \param [in,out] Str input stream of a CSV file
void readHeader(std::istream &Str) noexcept {
- std::string Line;
- std::getline(Str, Line);
- std::stringstream LineStream(Line);
- std::string Value;
+ std::string Line;
+ std::getline(Str, Line);
+ std::stringstream LineStream(Line);
+ std::string Value;
- while( getline(LineStream, Value, Delimeter) ){
- Header.push_back(Value);
- }
+ while (getline(LineStream, Value, Delimeter)) {
+ Header.push_back(Value);
+ }
- IsHeaderRead = true;
+ IsHeaderRead = true;
}
/// The number of rows to skip once.
///
/// This function returns the number of data rows to skip
/// at the beginning of the file.
///
/// \return The number of rows to skip at the beginning of a csv file.
- inline size_t SkipNumRows() const noexcept {
- return this->SkipRows;
- }
+ inline size_t SkipNumRows() const noexcept { return this->SkipRows; }
/// The current row number within the csv file.
///
/// This function returns the current row number. The header
/// row is not counted as a row.
///
/// \returns the current row number within the csv file.
- inline size_t CurRow() const noexcept {
- return this->RowNumber;
- }
+ inline size_t CurRow() const noexcept { return this->RowNumber; }
/// Indiciates if the header was already read.
///
/// This function returns true, if the header of a csv file which contains
/// a header file is already read.
/// The user has to pass in the attribute HeaderInfo the information if the
/// file has in the first row the header row or not.
///
/// \return if the header of a file is already read.
- inline bool IsHeaderReadDone() const noexcept{
- return this->IsHeaderRead;
- }
-
+ inline bool IsHeaderReadDone() const noexcept { return this->IsHeaderRead; }
/// Indicates if the file contains a header row in the first row.
///
/// This function returns if the file contains a header row.
- /// The information if the file contains a header row or not, has to be passed by the user.
+ /// The information if the file contains a header row or not, has to be passed
+ /// by the user.
/// The standard value is HeaderInformation::HasHeader
///
- /// \return if the csv file contains a header row in the first line of the file.
+ /// \return if the csv file contains a header row in the first line of the
+ /// file.
inline HeaderInformation HasFileHeader() const noexcept {
- return this->HeaderInfo;
+ return this->HeaderInfo;
}
/// Set the number of rows to skip.
///
/// This function sets the number of rows to skip at the beginning of
/// the reading of the file.
///
- /// \param SkipRows the number of rows you want to skip at the beginning of the file.
- inline void SetSkipRows(const size_t SkipRows) noexcept {
- this->SkipRows = SkipRows;
+ /// \param SkipRows the number of rows you want to skip at the beginning of
+ /// the file.
+ // NOTE (Maxi): I had to change "SkipRows" to "_SkipRows" because otherwise:
+ // "warning: C4458: declaration of 'SkipRows' hides class member"
+ inline void SetSkipRows(const size_t _SkipRows) noexcept {
+ this->SkipRows = _SkipRows;
}
/// Is the first row a header row or not.
///
/// This function sets the information, if the first row of the csv file
/// is a header line or not.
///
/// \param HeaderInfo if the first row is a header row or not.
- inline void SetHeaderInfo(const HeaderInformation HeaderInfo) noexcept {
- this->HeaderInfo = HeaderInfo;
+ // NOTE (Maxi): I had to change "HeaderInfo" to "_HeaderInfo", otherwise:
+ // "warning: C4458: declaration of 'HeaderInfo' hides class member"
+ inline void SetHeaderInfo(const HeaderInformation _HeaderInfo) noexcept {
+ this->HeaderInfo = _HeaderInfo;
}
/// Set the seperator between data entries.
///
- /// This funcction sets the separator between the data entries of the csv file.
+ /// This funcction sets the separator between the data entries of the csv
+ /// file.
///
/// \param Delimeter the character that separates the data values.
- inline void SetDelimeter(char Delimeter) {
- this->Delimeter = Delimeter;
- }
-
-
-
+ // NOTE (Maxi): I had to change "Delimeter" to "_Delimeter" because otherwise:
+ // "warning: C4458: declaration of 'Delimeter' hides class member"
+ inline void SetDelimeter(char _Delimeter) { this->Delimeter = _Delimeter; }
/// Gives a constant references for the \c std::tuple containing the values
/// read by \p this object.
///
/// \return \c CSVRow::Data
const std::tuple<Ts...> &tuple(void) const noexcept { return Data; }
private:
- std::tuple<Ts...> Data; ///< Stores parsed entries
- size_t SkipRows; ///< The number of rows to skip at the very beginning of the file.
- ///< This number only applies on the number of data rows.
- ///< If your file contains a header row and data rows, the skiping
- ///< of the header row is not taken into account.
- HeaderInformation HeaderInfo; ///< If the file contains a header row or not.
- char Delimeter; ///< The seperator between the data entries.
- size_t RowNumber; ///< Current row number, counts all row numbers including the header row.
- bool IsHeaderRead; ///< Was the header read or not.
- std::vector<std::string> Header; ///< The content of the header row.
-
+ std::tuple<Ts...> Data; ///< Stores parsed entries
+ size_t SkipRows; ///< The number of rows to skip at the very beginning of the
+ /// file.
+ ///< This number only applies on the number of data rows.
+ ///< If your file contains a header row and data rows, the skiping
+ ///< of the header row is not taken into account.
+ HeaderInformation HeaderInfo; ///< If the file contains a header row or not.
+ char Delimeter; ///< The seperator between the data entries.
+ size_t RowNumber; ///< Current row number, counts all row numbers including
+ /// the header row.
+ bool IsHeaderRead; ///< Was the header read or not.
+ std::vector<std::string> Header; ///< The content of the header row.
};
/// Reads a row of CSV data into \c rosa::csv::CSVRow.
///
/// The next line is read from \p Str by calling
/// \c rosa::csv::CSVRow::readNextRow on \p Data until all lines are
/// skipped.
///
/// If the function is called for the first time and the file contains
/// a header than is the header and the first data row read in after the
/// number of rows that the user wants to skip.
///
/// \tparam Ts type of values to read from the row
///
/// \note The CSV file should contain a line with fields matching \p Ts...
///
/// \param [in,out] Str input stream of a CSV file
/// \param [in,out] Data object to read the next line into
///
/// \return \p Str after reading one line from it
template <typename... Ts>
std::istream &operator>>(std::istream &Str, CSVRow<Ts...> &Data) {
- if( Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone() ) {
+ if (Data.HasFileHeader() == HeaderInformation::HasHeader &&
+ !Data.IsHeaderReadDone()) {
Data.readHeader(Str);
}
- while(Data.CurRow() < (Data.SkipNumRows())){
+ while (Data.CurRow() < (Data.SkipNumRows())) {
Data.readNextRow(Str);
}
- //read the lines after you skipped the number of rows you want to skip
+ // read the lines after you skipped the number of rows you want to skip
Data.readNextRow(Str);
return Str;
}
} // End namespace
/// Provides `InputIterator` features for iterating over a CSV file.
///
/// The iterator parses rows into `std::tuple` values and iterates over the
/// file row by row.
///
/// \tparam Ts types of values stored in one row of the CSV file
///
/// \note The iterator expects each row to consists of fields matching \p Ts.
///
/// \note The implementation relies on \c rosa::csv::CSVRow, which in turn
/// relies on \c rosa::csv::CSVRowParser, which is implemented only for
/// `arithmetic` types -- signed and unsigned integral types and floating-point
/// types -- and for \c std::string. Those are the valid values for \p Ts
template <typename... Ts> class CSVIterator {
public:
/// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator
///
/// Standard `typedef`s for iterators.
///
///@{
typedef std::input_iterator_tag
iterator_category; ///< Category of the iterator.
typedef std::tuple<Ts...> value_type; ///< Type of values iterated over.
typedef std::size_t difference_type; ///< Type to identify distance.
typedef std::tuple<Ts...> *pointer; ///< Pointer to the type iterated over.
typedef std::tuple<Ts...>
&reference; ///< Reference to the type iterated over.
///@}
/// Creates a new instance.
///
/// \param [in,out] S input stream to iterate over
- /// \param SkipRows the number of rows you want to skip only once at the beginning of the file.
- /// If you have an header in the file, it is supposed to be the first row, and it will be always read out.
- /// But after this header the next number of Rows will be skipped.
- /// \param HeaderInfo is used to know wheter the file contains an header row or not.
+ /// \param SkipRows the number of rows you want to skip only once at
+ /// the beginning of the file.
+ /// If you have an header in the file, it is supposed to be
+ /// the first row, and it will be always read out.
+ /// But after this header the next number of Rows will be
+ /// skipped.
+ /// \param HeaderInfo is used to know wheter the file contains an
+ /// header row or not.
/// The header has to be in the first row.
- /// \param Delimeter is the separator between the differnt values of the csv file.
+ /// \param Delimeter is the separator between the differnt values of
+ /// the csv file.
CSVIterator(std::istream &S, const size_t SkipRows = 0,
const HeaderInformation HeaderInfo = HeaderInformation::HasHeader,
- const char Delimeter = ',') : Str(S.good() ? &S : nullptr),
- SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row(){
+ const char Delimeter = ',')
+ : Str(S.good() ? &S : nullptr), SkipRows(SkipRows),
+ HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row() {
Row.SetSkipRows(SkipRows);
Row.SetHeaderInfo(HeaderInfo);
Row.SetDelimeter(Delimeter);
// \c rosa::csv::CSVIterator::Row is initialized empty so the first
// incrementation here will read the first row.
++(*this);
}
/// Creates an empty new instance.
CSVIterator(void) noexcept : Str(nullptr) {}
/// Pre-increment operator.
///
/// The implementation reads the next row. If the end of the input stream is
/// reached, the operator becomes empty and has no further effect.
///
/// \return \p this object after incrementing it.
CSVIterator &operator++() {
if (Str) {
if (!((*Str) >> Row)) {
Str = nullptr;
}
}
return *this;
}
/// Post-increment operator.
///
/// The implementation uses the pre-increment operator and returns a copy of
/// the original state of \p this object.
///
/// \return \p this object before incrementing it.
CSVIterator operator++(int) {
CSVIterator Tmp(*this);
++(*this);
return Tmp;
}
/// Returns a constant reference to the current entry.
///
/// \note Should not dereference the iterator when it is empty.
///
/// \return constant reference to the current entry.
const std::tuple<Ts...> &operator*(void)const noexcept { return Row.tuple(); }
/// Returns a constant pointer to the current entry.
///
/// \note Should not dereference the iterator when it is empty.
///
/// \return constant pointer to the current entry.
const std::tuple<Ts...> *operator->(void)const noexcept {
return &Row.tuple();
}
/// Tells if \p this object is equal to another one.
///
/// Two \c rosa::csv::CSVReader instances are equal if and only if they are
/// the same or both are empty.
///
/// \param RHS other object to compare to
///
/// \return whether \p this object is equal with \p RHS
bool operator==(const CSVIterator &RHS) const noexcept {
return ((this == &RHS) || ((this->Str == nullptr) && (RHS.Str == nullptr)));
}
/// Tells if \p this object is not equal to another one.
///
/// \see rosa::csv::CSVReader::operator==
///
/// \param RHS other object to compare to
///
/// \return whether \p this object is not equal with \p RHS.
bool operator!=(const CSVIterator &RHS) const noexcept {
return !((*this) == RHS);
}
/// Set the delimeter used in the csv file.
/// \param Delimeter the character which separates the values in the csv file.
- inline void setDelimeter(char Delimeter) noexcept {
- this->Delimeter = Delimeter;
+ // NOTE (Maxi): I had to change "Delimeter" to "_Delimeter" because otherwise:
+ // "warning: C4458: declaration of 'Delimeter' hides class member"
+ inline void setDelimeter(char _Delimeter) noexcept {
+ this->Delimeter = _Delimeter;
}
/// get the delimeter currently set to separate the values in the csv file.
- /// \return the current character, which is used to separte teh values in the csv file.
- inline char getDelimeter() const noexcept {
- return this->Delimeter;
- }
+ /// \return the current character, which is used to separte teh values in the
+ /// csv file.
+ inline char getDelimeter() const noexcept { return this->Delimeter; }
private:
- std::istream *Str; ///< Input stream of a CSV file to iterate over.
- size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the file.
- HeaderInformation HeaderInfo; ///< does the csv file contain a header or not, if this information is
- ///< not given correclty, the reading of the header would result in
- ///< in an error.
- char Delimeter; ///< Delimeter between the entries in the csv file.
- CSVRow<Ts...> Row; ///< Content of the current row
-
+ std::istream *Str; ///< Input stream of a CSV file to iterate over.
+ size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the
+ /// file.
+ HeaderInformation HeaderInfo; ///< does the csv file contain a header or not,
+ /// if this information is
+ ///< not given correclty, the reading of the header would result in
+ ///< in an error.
+ char Delimeter; ///< Delimeter between the entries in the csv file.
+ CSVRow<Ts...> Row; ///< Content of the current row
};
} // End namespace csv
} // End namespace rosa
#endif // ROSA_SUPPORT_CSV_CSVREADER_HPP
diff --git a/include/rosa/support/writer/split_tuple_writer.hpp b/include/rosa/support/writer/split_tuple_writer.hpp
index d98ace2..f8701b3 100644
--- a/include/rosa/support/writer/split_tuple_writer.hpp
+++ b/include/rosa/support/writer/split_tuple_writer.hpp
@@ -1,562 +1,559 @@
//===-- rosa/support/writer/split_tuple_writer.hpp ---------------*- C++ -*-===/
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===/
///
/// \file rosa/support/writer/split_tuple_writer.hpp
///
/// \authors David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Facilities to split writers of \c std::tuple into writers of
/// their elements.
///
//===----------------------------------------------------------------------===/
#ifndef ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
#define ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
#include "rosa/support/debug.hpp"
+#include "rosa/support/log.h"
#include "rosa/support/sequence.hpp"
#include <memory>
-#include <tuple>
#include <queue>
+#include <tuple>
namespace rosa {
namespace writer {
/// What to do when splitted element writers got destructed so that some
/// incomplete tuples remain in the buffer.
enum IncompleteTuplePolicy {
Ignore, ///< ignore incomplete tuples, data discarded
Flush, ///< flush incomplete tuples, use default value for missing elements
#ifndef NDEBUG
DbgFail ///< fail with an assertion, available only in debug build
-#endif // !defined NDEBUG
+#endif // !defined NDEBUG
};
/// Anonymous namespace providing implementation for splitting tuple writers;
/// consider it private.
namespace {
/// Writes values into a buffer based on an index.
///
/// The values are first buffered and flushed out later once a corresponding
/// tuple gets complete (i.e., other elementwise writers put the corresponding
/// values into the buffer). Since the underlying buffer waits for tuples to
/// become complete, the elementwise writers are expected to provide the same
/// number of values for the buffer (i.e., completing all tuples that have an
/// element). That is, however, not enforced in any ways.
///
/// Once all elementwise writers are destructed, the underlying buffer gets
/// destructed as well. Should the buffer contain some values, which are
/// elements of incomplete tuples, the destructor of the buffer handles the
/// situation according to the \c rosa::writer::IncompleteTuplePolicy set when
/// the elementwise writers were created by \c rosa::writer::splitTupleWriter().
///
/// \tparam TB buffer to write into
/// \tparam I index of the values to write to \p TB
///
/// \note \p TB is expected to implemnet an interface matching that of \c
/// TupleWriterBuffer.
///
/// \note the
-template <typename TB, size_t I>
-class SplittedElementWriter {
+template <typename TB, size_t I> class SplittedElementWriter {
public:
/// Type alias for the values the writer writes.
using T = typename TB::template element_type<I>;
- /// \defgroup SplittedElemenWriterTypedefs Typedefs of
+ /// \defgroup SplittedElemenWriterTypedefs Typedefs of
/// \c SplittedElementWriter
///
/// Useful `typedef`s for writers.
///
///@{
typedef T value_type; ///< Type of values written.
typedef T &reference; ///< Reference to the type written
///@}
/// Creates a new instance.
///
/// \param Buffer buffer \p this object writes values into
///
/// \note \p Buffer is captured as a \c std::shared_ptr because it is shared
/// among writers for its element positions.
SplittedElementWriter(std::shared_ptr<TB> Buffer) : Buffer(Buffer) {}
/// Tells how many values are still in the buffer.
///
/// Values are buffered as long as all elements of their corresponding tuples
/// are written into the buffer by other elementwise writers and in turn
/// complete tuples written by the buffer itself. This function tells how many
/// values are still in the buffer waiting for their containing tuples to be
/// complete and written out from the buffer.
///
/// \note The function simply calles the corresponding function of \p TB.
///
/// \return how many values are still in the buffer.
size_t buffered(void) const noexcept {
return Buffer->template buffered<I>();
}
/// Tells if the last write operation of the buffer was successful.
///
/// Values are buffered until their corresponding tuples gets complete. The
/// buffer writes complete tuples by the underlying writer. This function
/// tells if the buffer successfully has written the latest complete tuple by
/// the underlying writer.
///
/// Once this function returns \c false, further \c write() calls has no
/// effect and the last \c buffered() values will not be written to the
/// underlying writer of \c std::tuple.
///
/// \note The function simply calles the corresponding function of \p TB.
///
/// \return if the last write operation was successful
bool good(void) const noexcept { return Buffer->good(); }
/// Writes an entry to the buffer.
///
/// The function has no effect if an earlier write operation of the buffer has
/// failed, that is \c good() returns \c false.
///
/// \note The function simply calles the corresponding function of \p TB.
///
/// \param V value to write
void write(const T &V) { Buffer->template write<I>(V); }
private:
std::shared_ptr<TB> Buffer; ///< Buffer \p this object writes into
};
/// Writes an element of a tuple.
///
/// \see SplittedElementWriter
///
/// \tparam TB type of the buffer \p W writes into
/// \tparam I index of the values \p W write to \p TB
///
/// \param [in,out] W object to write with
/// \param V value to write
///
/// \return \p W after writing \p V with it
template <typename TB, size_t I>
SplittedElementWriter<TB, I> &
operator<<(SplittedElementWriter<TB, I> &W,
const typename SplittedElementWriter<TB, I>::value_type &V) {
W.write(V);
return W;
}
///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of
-///types into a \c std::tuple of container of those types.
+/// types into a \c std::tuple of container of those types.
///
/// The new type is used for buffering elements of tuples separately.
///
///@{
/// Template declaration.
///
/// \tparam T type to convert
///
/// \note The template is defined only when \p T is a \c std::tuple.
///
/// Usage for a type \c Tuple as:\code
/// typename TupleBufferContainer<Tuple>::Type
/// \endcode
template <typename T> struct TupleBufferContainer;
/// Template definition for \c std::tuple.
template <typename... Ts> struct TupleBufferContainer<std::tuple<Ts...>> {
/// The converted type.
using Type = std::tuple<std::queue<Ts>...>;
};
///@}
/// Convenience template type alias for easy use of \c TupleBufferContainer.
///
/// Converts a \c std::tuple of types into a \c std::tuple of container of those
/// types.
///
/// \tparam Tuple type to convert
template <typename Tuple>
using tuple_buffer_container_t = typename TupleBufferContainer<Tuple>::Type;
/// Buffer for element values for writer of \c std::tuple.
///
/// The class can be instantiated with a writer of \c std::tuple and provides
/// an interface for writing elements into tuples one by one.
///
/// \note The class is utilized by \c SplittedElementWriter.
///
/// \tparam TupleWriter the writer type that handles values of \c std::tuple
///
/// \note The elements of the written \c std::tuple need to be default
/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush.
///
/// \todo Consider thread safety of the class.
-template <typename TupleWriter>
-class TupleWriterBuffer {
+template <typename TupleWriter> class TupleWriterBuffer {
public:
/// Type alias for the value type of \p TupleWriter.
/// \note The type is expected to be \c std::tuple.
using writer_value_type = typename TupleWriter::value_type;
/// The number of elements of \c writer_value_type.
static constexpr size_t writer_value_size =
std::tuple_size_v<writer_value_type>;
/// Template type alias to get element types of \c writer_value_type by
/// index.
/// \tparam I the index of the element
template <size_t I>
- using element_type =
- typename std::tuple_element<I, writer_value_type>::type;
+ using element_type = typename std::tuple_element<I, writer_value_type>::type;
/// Type alias for index sequence for accessing elements of \c
/// writer_value_size.
using element_idx_seq_t = seq_t<writer_value_size>;
/// Creates a new instance.
///
/// \param Writer the writer \p this object uses to write tuples
/// \param Policy what to do with incomplete tuples when destructing \p
/// this object
TupleWriterBuffer(TupleWriter &&Writer,
const IncompleteTuplePolicy Policy) noexcept
: Writer(Writer), Policy(Policy), Buffer() {}
/// Destructor.
///
/// Should \p this object has some values in \c Buffer (i.e., incomplete
/// tuples), the destructor takes care of them according to \c Policy.
///
/// \note The destructor may flush the buffered values if the last write
/// operation was successful, that is \c good() returns \c true.
~TupleWriterBuffer(void) noexcept {
if (good()) {
switch (Policy) {
default:
- ASSERT(false && "Unexpected Policy");
+ LOG_ERROR("Unexpected Policy");
case Ignore:
// Do not care about incomplete tuples in the buffer.
break;
case Flush:
// Whatever we have in the buffer, flush it out.
flush();
break;
#ifndef NDEBUG
case DbgFail:
ASSERT(empty(element_idx_seq_t()) && "Non-empty buffer");
break;
#endif // !defined NDEBUG
}
}
}
/// Tells how many values are in the buffer for index \p I.
///
/// \tparam I the index of the element to check
///
/// \return the number of values in buffer for index \p I
template <size_t I> size_t buffered(void) const noexcept {
return std::get<I>(Buffer).size();
}
/// Tells if the last write operation was successful.
///
/// Values are buffered until their corresponding tuples gets complete.
/// Once a tuple becomes complete, it is written by \c Writer. This function
/// tells if writing the latest complete tuple was successful.
///
/// Once this function returns \c false, further \c write<I>() calls has no
/// effect and the last \c buffered<I>() values will not be written to the
/// underlying writer of \c std::tuple.
///
/// \return if the last write operation was successful
bool good(void) const noexcept { return Writer.good(); }
/// Writes an entry to the buffer for index \p I.
///
/// The function has no effect if an earlier write operation of the buffer has
/// failed, that is \c good() returns \c false.
///
/// The value is buffered first and written by \p Writer once its
/// corresponding tuple becomes complete (i.e., all other elements of the
/// tuple are written into \p this object by corresponding elementwise
/// writers).
///
/// \tparam I the index of the element to write
///
/// \param V value to write
template <size_t I> void write(const element_type<I> &V) {
ASSERT(!hasCompleteTuple(element_idx_seq_t()));
if (good()) {
std::get<I>(Buffer).push(V);
if (hasCompleteTuple(element_idx_seq_t())) {
writeTuple(false);
}
}
ASSERT(!hasCompleteTuple(element_idx_seq_t()));
}
private:
/// Tells whether \c Buffer is completely empty, all elements of it are empty.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
///
/// \return whether all elements of \c Buffer are empty
template <size_t... S0> bool empty(Seq<S0...>) const noexcept {
return (true && ... && std::get<S0>(Buffer).empty());
}
/// Tells whether \c Buffer contains at least one complete tuple.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
///
/// \return whether there is at least one complete tuple in \c Buffer
template <size_t... S0> bool hasCompleteTuple(Seq<S0...>) const noexcept {
return (true && ... && !std::get<S0>(Buffer).empty());
}
/// Makes sure \c Buffer has one complete tuple in front.
///
/// If the tuple in front of \c Buffer is not complete, missing elements are
/// default constructed in \c Buffer to complete the tuple.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
template <size_t... S0> void makeCompleteTuple(Seq<S0...>) noexcept {
auto addDefaultIfEmpty = [](auto &Queue) {
if (Queue.empty()) {
Queue.emplace(); // default construct a value in the empty queue.
}
};
(addDefaultIfEmpty(std::get<S0>(Buffer)), ...);
}
/// Gives the first complete tuple from \c Buffer.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
///
/// \return the first complete tuple from \c Buffer
///
/// \pre There is at least one complete tuple in \c Buffer:\code
/// hasCompleteTuple(element_idx_seq_t())
/// \endcode
template <size_t... S0> writer_value_type front(Seq<S0...>) const noexcept {
ASSERT(hasCompleteTuple(element_idx_seq_t()));
return {std::get<S0>(Buffer).front()...};
}
/// Removes the first complete tuple from \c Buffer.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
///
/// \pre There is at least one complete tuple in \c Buffer:\code
/// hasCompleteTuple(element_idx_seq_t())
/// \endcode
template <size_t... S0> void pop(Seq<S0...>) noexcept {
ASSERT(hasCompleteTuple(element_idx_seq_t()));
(std::get<S0>(Buffer).pop(), ...);
}
/// Takes the first tuple from \c Buffer and writes it with \c Writer.
///
/// \c Buffer need to contain a complete tuple to write it with \c Writer. The
/// function makes sure that the tuple in front of \c Buffer is complete if \p
/// MakeComplete is true. The tuple in fron of \c Buffer are expected to be
/// complete otherwise.
///
/// \param MakeComplete whether to make the first tuple complete
///
/// \pre There is at least one complete tuple in \c Buffer if not \p
/// MakeComplete: \code
/// MakeComplete || hasCompleteTuple(element_idx_seq_t())
/// \endcode
void writeTuple(const bool MakeComplete) noexcept {
if (MakeComplete) {
makeCompleteTuple(element_idx_seq_t());
}
ASSERT(hasCompleteTuple(element_idx_seq_t()));
Writer.write(front(element_idx_seq_t()));
pop(element_idx_seq_t());
}
/// Empties \c Buffer by writing out all tuples from it so that missing
/// elements of incomplete tuples are extended with default constructed
/// values.
void flush(void) noexcept {
while (!empty(element_idx_seq_t())) {
ASSERT(!hasCompleteTuple(element_idx_seq_t())); // Sanity check.
writeTuple(true);
}
}
TupleWriter Writer; ///< The splitted writer
const IncompleteTuplePolicy Policy; ///< what to do with incomplete tuples
///< when destructing \p this object
tuple_buffer_container_t<writer_value_type>
Buffer; ///< Container for elementwise buffering
};
/// Template type alias for writer of an element based on buffered writer of
/// \c std::tuple.
///
/// The alias utilizes \c SplittedElementWriter for writing element values by
/// \p TB.
///
/// \tparam TB buffer to write into
/// \tparam I index of the values to write to \p TB
template <typename TB, size_t I>
using element_writer_t = SplittedElementWriter<TB, I>;
///\defgroup ElementWriters Type converter turning a buffer of \c std::tuple
-///into a \c std::tuple of corresponding \c element_writer_t.
+/// into a \c std::tuple of corresponding \c element_writer_t.
///
///@{
/// Template declaration.
///
/// \tparam TB buffer to write into
/// \tparam S type providing indices for accessing elements of \c std::tuple
///
/// \note \p TB is expected to implement an interface matching that of \c
/// TupleWriterBuffer.
///
/// \note The template is defined only when \p S is a \c rosa::Seq.
///
/// Usage for a proper buffer type \c TB:\code
/// typename ElementIteratorWriters<TB, typename TB::element_idx_seq_t>::Type
/// \endcode
template <typename TB, typename S> struct ElementWriters;
/// Template definition.
-template <typename TB, size_t... S0>
-struct ElementWriters<TB, Seq<S0...>> {
+template <typename TB, size_t... S0> struct ElementWriters<TB, Seq<S0...>> {
/// The converted type.
using Type = std::tuple<element_writer_t<TB, S0>...>;
};
///@}
/// Convenience template type alias for easy use of \c ElementIteratorWriters.
///
/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c
/// element_writer_t.
///
/// \tparam TB buffer to write into
///
/// \note \p TB is expected to implement an interface matching that of \c
/// TupleWriterBuffer.
template <typename TB>
using element_writers_t =
typename ElementWriters<TB, typename TB::element_idx_seq_t>::Type;
/// Template type alias for turning a writer of \c std::tuple into
/// corresponding \c element_writers_t.
///
/// The alias utilizes \c TupleWriterBuffer for buffering values before writing
/// them by \p TupleWriter. The elementwise writers are \c
/// SplittedElementWriter.
///
/// \tparam TupleWriter writer of \c std::tuple
template <typename TupleWriter>
using splitted_tuple_writers_t =
element_writers_t<TupleWriterBuffer<TupleWriter>>;
/// Creates elementwise writers for a writer of \c std::tuple.
///
/// \note Implementation for \c rosa::iterator::splitTupleWriter.
///
/// \tparam TupleWriter writer of \c std::tuple
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \param Writer the writer to write tuples with
/// \param Policy how to handle incomplete tuples when destructing the
/// underlying buffer
///
/// \note The last parameter provides indices as template parameter \p S0 and
/// its actual value is ignored.
///
/// \return \c std::tuple of elementwise writers corresponding to \p Writer
template <typename TupleWriter, size_t... S0>
splitted_tuple_writers_t<TupleWriter>
splitTupleWriterImpl(TupleWriter &&Writer, const IncompleteTuplePolicy Policy,
Seq<S0...>) noexcept {
using TB = TupleWriterBuffer<TupleWriter>;
// Create a buffer in shared pointer. The buffer will be destructed once
// all corresponding element writers (created below) have been destructed.
auto Buffer = std::make_shared<TB>(std::move(Writer), Policy);
return {element_writer_t<TB, S0>(Buffer)...};
}
} // End namespace
/// Creates elementwise writers for a writer of \c std::tuple.
///
/// \note The implementation utilizes \c splitTupleWriterImpl.
///
/// Obtain elementwise writers for a writer \p Writer of type \c TupleWriter of
/// \c std::tuple<T1, T2, T3> as \code
/// auto [T1Writer, T2Writer, T3Writer] = splitTupleWriter(std::move(Writer),
/// Ignore);
/// \endcode
///
/// The function returns a tuple of writers, which can be assigned to variables
/// using structured binding.
///
/// The function moves its first argument (i.e., \p Writer cannot be used
/// directly after splitting it). If the first argument is a variable, it needs
/// to be moved explicitly by \c std::move() as in the example.
///
/// While a tuple could be written with \p Writer directly as \code
/// Writer << std::make_tuple(T1(), T2(), T3());
/// \endcode The same tuple is written with splitted writers as \code
/// T1Writer << T1();
/// T2Writer << T2();
/// T3Writer << T3();
/// \endcode
///
/// \tparam TupleWriter writer of \c std::tuple
///
/// \note The elements of the written \c std::tuple need to be default
/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush.
///
/// \param Writer the writer to write tuples with
/// \param Policy what to do with incomplete tuples when destructing the
/// underlying buffer
///
/// \return \c std::tuple of elementwise writers corresponding to \p Writer
template <typename TupleWriter>
splitted_tuple_writers_t<TupleWriter>
splitTupleWriter(TupleWriter &&Writer,
const IncompleteTuplePolicy Policy) noexcept {
return splitTupleWriterImpl(
std::move(Writer), Policy,
seq_t<std::tuple_size_v<typename TupleWriter::value_type>>());
}
} // End namespace writer
} // End namespace rosa
#endif // ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Oct 20, 1:31 PM (1 d, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
199730
Default Alt Text
(161 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment