Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F1497786
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Size
212 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp
index c65342c..d71d885 100644
--- a/apps/ccam/ccam.cpp
+++ b/apps/ccam/ccam.cpp
@@ -1,305 +1,307 @@
//===-- 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);
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);
}
std::shared_ptr<PartialFunction<float, float>> BrokenDelayFunction(
new PartialFunction<float, float>(
{{{0, AppConfig.BrokenCounter},
std::make_shared<LinearFunction<float, 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));
std::shared_ptr<PartialFunction<float, float>> OkDelayFunction(
new PartialFunction<float, float>(
{{{0, AppConfig.BrokenCounter},
std::make_shared<LinearFunction<float, 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));
//
// Create a DeluxeAgent with SystemStateDetector functionality.
//
LOG_INFO("Create SystemStateDetector agent.");
AgentHandle SystemStateDetector = createSystemStateDetectorAgent(
C, "SystemStateDetector", AppConfig.SignalConfigurations.size(),
BrokenDelayFunction, OkDelayFunction);
// The new agent logs its input values and results in the the sum of them.
/** AgentHandle BodyAgent = C->createAgent(
"Body Agent",
DeluxeAgent::D<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
uint32_t>(
[](std::pair<uint32_t, bool> HR, std::pair<uint32_t, bool> BR,
std::pair<uint32_t, bool> SpO2, std::pair<uint32_t, bool> BPSys,
std::pair<uint32_t, bool> BodyTemp) -> Optional<uint32_t> {
LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n"
<< (HR.second ? "<New>" : "<Old>")
<< " HR warning score: " << HR.first << "\n"
<< (BR.second ? "<New>" : "<Old>")
<< " BR warning score: " << BR.first << "\n"
<< (SpO2.second ? "<New>" : "<Old>")
<< " SpO2 warning score: " << SpO2.first << "\n"
<< (BPSys.second ? "<New>" : "<Old>")
<< " BPSys warning score: " << BPSys.first << "\n"
<< (BodyTemp.second ? "<New>" : "<Old>")
<< " BodyTemp warning score: " << BodyTemp.first
<< "\n******\n";
return {HR.first + BR.first + SpO2.first + BPSys.first +
BodyTemp.first};
}));
*/
//
// Connect low-level agents to the high-level agent.
//
LOG_INFO("Connect low-level agents to the high-level agent.");
/// C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel");
//
// 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 ScoreCSV(ScoreCSVPath);
/// csv::CSVWriter<uint32_t> ScoreWriter(ScoreCSV);
// The agent writes each new input value into a CSV file and produces nothing.
/** AgentHandle LoggerAgent = C->createAgent(
"Logger Agent",
DeluxeAgent::D<unit_t, uint32_t>(
[&ScoreWriter](std::pair<uint32_t, bool> Score) -> Optional<unit_t> {
if (Score.second) {
// The state of \p ScoreWriter is not checked, expecting good.
ScoreWriter << Score.first;
}
return {};
}));
*/
//
// Connect the high-level agent to the logger agent.
//
LOG_INFO("Connect the high-level agent to the logger agent.");
/// C->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent 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(NumberOfSimulationCycles);
return 0;
}
diff --git a/apps/ccam/cheatsheet.c b/apps/ccam/cheatsheet.c
new file mode 100644
index 0000000..749478d
--- /dev/null
+++ b/apps/ccam/cheatsheet.c
@@ -0,0 +1,92 @@
+#include "rosa/deluxe/DeluxeContext.hpp"
+
+std::unique_ptr<DeluxeContext> C = DeluxeContext::create("Deluxe");
+// sensor
+C->createSensor<Input, output>(name,
+ // lambda function
+ [](std::pair<input, bool> I) -> void {
+ // automatically pushes its value to the
+ // connected agent
+ });
+
+// ---------------------------------------------------------------------------
+// communication with 1 other agent
+// ---------------------------------------------------------------------------
+// Handlerfunctions have to be the corresponding std::functions
+
+// singe input singe output
+C->createAgent(Name,
+ Handlerfunction(
+ // lambda function
+ [](std::pair<InputType, bool> I) -> optional<ResultType> {
+ //...
+ // return {value};
+ }));
+
+// multi input single output
+C->createAgent(Name,
+ Handlerfunction(
+ // lambda function
+ [](std::pair<DeluxeTuple<InputType1, InputType2>, bool> I)
+ -> optional<DeluxeTuple<ResultType>> {
+ //...
+ // DeluxeTuple<ResultType> output(value);
+ // return {output};
+ }));
+
+// single input multi output
+C->createAgent(Name,
+ Handlerfunction(
+ // lambda function
+ [](std::pair<DeluxeTuple<InputType>, bool> I)
+ -> optional<DeluxeTuple<ResultType1, ResultType2>> {
+ //...
+ // DeluxeTuple<ResultType1, ResultType2>
+ // output(value1,value2);
+ // return {output};
+ }));
+
+// multi in/out and handles results from master
+C->createAgent(
+ Name,
+ // Master-input handler.
+ MasterHandlerfunction(
+ // lambda function
+ [](std::pair<DeluxeTuple<feedbackType>, bool> I)
+ -> void { // you can again return something to the master but i
+ // don't know at the moment the changes that would cause
+ //
+ //..
+ }),
+ // input handler.
+ Handlerfunction(
+ // lambda function
+ [](std::pair<DeluxeTuple<InputType1, InputType2>, bool> I)
+ -> optional<DeluxeTuple<ResultType1, ResultType2>> {
+ //...
+ // DeluxeTuple<ResultType1, ResultType2>
+ // output(value1,value2);
+ // return {output};
+ }));
+
+// ---------------------------------------------------------------------------
+// communication with n other agent
+// ---------------------------------------------------------------------------
+
+// I don't know how it reacts if input 1 is a DeluxeTuple and input 2 is just a
+// value I'm just using all with DeluxeTuples
+C->createAgent(
+ Name, Handlerfunction(
+ // lambda function
+ [](std::pair<DeluxeTuples<SlaveOutputs1...>, bool> I0,
+ std::pair<DeluxeTuples<SlaveOutputs2...>, bool> I1,
+ std::pair<DeluxeTuples<SlaveOutputs3...>, bool> I2,
+ ...) -> std::tuple<Optional<DeluxeTuple<give_to_master>>,
+ Optional<DeluxeTuple<give_to_slave1>>,
+ Optional<DeluxeTuple<give_to_slave2>>,
+ Optional<DeluxeTuple<give_to_slave3>>, ...>;
+ {
+ // ...
+ // return {{for_master}, {for_slave_1}, {for_slave_1},
+ // {for_slave_1}};
+ }));
\ No newline at end of file
diff --git a/apps/ccam/statehandlerutils.h b/apps/ccam/statehandlerutils.h
index 5eaf47f..0d33d6a 100644
--- a/apps/ccam/statehandlerutils.h
+++ b/apps/ccam/statehandlerutils.h
@@ -1,182 +1,182 @@
#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, float, uint8_t, uint32_t,
- bool, bool, bool, bool>;
+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,
- (uint8_t)StateInfo.SignalStateCondition,
+ StateInfo.SignalStateCondition,
StateInfo.NumberOfInsertedSamplesAfterEntrance,
StateInfo.SignalStateIsValid,
StateInfo.SignalStateJustGotValid,
- StateInfo.SignalStateIsValidAfterReentrance,
- StateInfo.SignalIsStable};
+ 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;
+ // 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) {
(void)BrokenDelayFunction;
(void)OkDelayFunction;
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,
- NumOfSlaves, NumOfSlaves>>
- SysSD(new SystemStateDetector<float, float, float, HistoryPolicy::FIFO,
- NumOfSlaves, NumOfSlaves>(
+ std::shared_ptr<SystemStateDetector<float, float, float, HistoryPolicy::FIFO>>
+ SysSD(new SystemStateDetector<float, 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) {
switch (NumOfSlaves) {
// 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/include/rosa/agent/FunctionAbstractions.hpp b/include/rosa/agent/FunctionAbstractions.hpp
index a7442c6..c2e681e 100644
--- a/include/rosa/agent/FunctionAbstractions.hpp
+++ b/include/rosa/agent/FunctionAbstractions.hpp
@@ -1,360 +1,360 @@
//===-- rosa/agent/FunctionAbstractions.hpp ---------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/FunctionAbstractions.hpp
///
/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *FunctionAbstractions* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP
#define ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP
#include "rosa/agent/Abstraction.hpp"
#include "rosa/agent/Functionality.h"
#include "rosa/support/debug.hpp"
#include <algorithm>
#include <cmath>
#include <memory>
#include <vector>
namespace rosa {
namespace agent {
//@benedikt: check if your partialfunctions can take infinity as
// argument
/// Implements \c rosa::agent::Abstraction as a linear function,
/// y = Coefficient * X + Intercept.
///
/// \note This implementation is supposed to be used to represent a linear
/// function from an arithmetic domain to an arithmetic range. This is enforced
/// statically.
///
/// \tparam D type of the functions domain
/// \tparam R type of the functions range
template <typename D, typename R>
class LinearFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
"LinearFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::value),
"LinearFunction not to arithmetic");
protected:
/// The Intercept of the linear function
- const D Intercept;
+ const R Intercept;
/// The Coefficient of the linear function
- const D Coefficient;
+ const R Coefficient;
public:
/// Creates an instance.
///
/// \param Intercept the intercept of the linear function
/// \param Coefficient the coefficient of the linear function
- LinearFunction(D Intercept, D Coefficient) noexcept
+ LinearFunction(R Intercept, R Coefficient) noexcept
: Abstraction<D, R>(Intercept), Intercept(Intercept),
Coefficient(Coefficient) {}
/// Creates an instance given the two points on a linear function.
///
/// \param x1 The x-value of the first point
/// \param y1 The x-value of the first point
/// \param x2 The y-value of the second point
/// \param y2 The y-value of the second point
LinearFunction(D x1, R y1, D x2, R y2) noexcept
: LinearFunction<D, R>(y1 - x1 * (y1 - y2) / (x1 - x2),
(y1 - y2) / (x1 - x2)) {}
/// Creates an instance given the two points on a linear function.
///
/// \param p1 The coordinates of the first point
/// \param p2 The coordinates of the second point
LinearFunction(std::pair<D, R> p1, std::pair<D, R> p2) noexcept
: LinearFunction<D, R>(p1.first, p1.second, p2.first, p2.second) {}
/// Destroys \p this object.
~LinearFunction(void) = default;
/// Checks wether the Abstraction evaluates to default at the given position
/// As LinearFunctions can be evaluated everythwere, this is always false
///
/// \param V the value at which to check if the function falls back to it's
/// default value.
///
/// \return false
bool isDefaultAt(const D &V) const noexcept override {
(void)V;
return false;
}
/// Getter for member variable Intercept
///
/// \return Intercept
D getIntercept() const { return Intercept; }
/// Setter for member variable Intercept
///
/// \param Intercept the new Intercept
void setIntercept(const D &Intercept) { this->Intercept = Intercept; }
/// Getter for member variable Coefficient
///
/// \return Coefficient
D getCoefficient() const { return Coefficient; }
/// Setter for member variable Coefficient
///
/// \param Coefficient the new Intercept
void setCoefficient(const D &Coefficient) { this->Coefficient = Coefficient; }
/// Set Intercept and Coefficient from two points on the linear function
///
/// \param x1 The x-value of the first point
/// \param y1 The x-value of the first point
/// \param x2 The y-value of the second point
/// \param y2 The y-value of the second point
void setFromPoints(D x1, R y1, D x2, R y2) {
Coefficient = (y1 - y2) / (x1 - x2);
Intercept = y1 - Coefficient * x1;
}
/// Set Intercept and Coefficient from two points on the linear function
///
/// \param p1 The coordinates of the first point
/// \param p2 The coordinates of the second point
inline void setFromPoints(std::pair<D, R> p1, std::pair<D, R> p2) {
setFromPoints(p1.first, p1.second, p2.first, p2.second);
}
/// Evaluates the linear function
///
/// \param X the value at which to evaluate the function
///
/// \return Coefficient*X + Intercept
virtual R operator()(const D &X) const noexcept override {
return Intercept + X * Coefficient;
}
};
/// Implements \c rosa::agent::Abstraction as a sine function,
/// y = Amplitude * sin(Frequency * X + Phase) + Average.
///
/// \note This implementation is supposed to be used to represent a sine
/// function from an arithmetic domain to an arithmetic range. This is enforced
/// statically.
///
/// \tparam D type of the functions domain
/// \tparam R type of the functions range
template <typename D, typename R>
class SineFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
"SineFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::value),
"SineFunction not to arithmetic");
protected:
/// The frequency of the sine wave
const D Frequency;
/// The Ampiltude of the sine wave
const D Amplitude;
/// The Phase-shift of the sine wave
const D Phase;
/// The y-shift of the sine wave
const D Average;
public:
/// Creates an instance.
///
/// \param Frequency the frequency of the sine wave
/// \param Amplitude the amplitude of the sine wave
/// \param Phase the phase of the sine wave
/// \param Average the average of the sine wave
SineFunction(D Frequency, D Amplitude, D Phase, D Average) noexcept
: Abstraction<D, R>(Average), Frequency(Frequency), Amplitude(Amplitude),
Phase(Phase), Average(Average) {}
/// Destroys \p this object.
~SineFunction(void) = default;
/// Checks wether the Abstraction evaluates to default at the given position
/// As SineFunctions can be evaluated everythwere, this is always false
///
/// \param V the value at which to check if the function falls back to it's
/// default value.
///
/// \return false
bool isDefaultAt(const D &V) const noexcept override {
(void)V;
return false;
}
/// Evaluates the sine function
///
/// \param X the value at which to evaluate the function
/// \return the value of the sine-function at X
virtual R operator()(const D &X) const noexcept override {
return Amplitude * sin(Frequency * X + Phase) + Average;
}
};
enum StepDirection { StepUp, StepDown };
/// Implements \c rosa::agent::PartialFunction as a step function from 0 to 1
/// with a ramp in between
///
/// \tparam D type of the functions domain
/// \tparam R type of the functions range
template <typename D, typename R>
class StepFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value), "abstracting not arithmetic");
STATIC_ASSERT((std::is_arithmetic<R>::value),
"abstracting not to arithmetic");
private:
D Coefficient;
D RightLimit;
StepDirection Direction;
public:
/// Creates an instance by Initializing the underlying \c Abstraction.
///
/// \param Coefficient Coefficient of the ramp
/// \param Direction wether to step up or down
///
/// \pre Coefficient > 0
StepFunction(D Coefficient, StepDirection Direction = StepUp)
: Abstraction<D, R>(0), Coefficient(Coefficient),
RightLimit(1.0f / Coefficient), Direction(Direction) {
ASSERT(Coefficient > 0);
}
/// Destroys \p this object.
~StepFunction(void) = default;
/// Setter for Coefficient
///
/// \param Coefficient the new Coefficient
void setCoefficient(const D &Coefficient) {
ASSERT(Coefficient > 0);
this->Coefficient = Coefficient;
this->RightLimit = 1 / Coefficient;
}
/// Setter for RightLimit
///
/// \param _RightLimit the new RightLimit
void setRightLimit(const D &_RightLimit) {
ASSERT(_RightLimit > 0);
this->RightLimit = _RightLimit;
this->Coefficient = 1 / _RightLimit;
}
/// Checks wether the Abstraction evaluates to default at the given position
///
/// \param V the value at which to check if the function falls back to it's
/// default value.
///
/// \return false if the is negative, true otherwise
bool isDefaultAt(const D &V) const noexcept override { return V > 0; }
/// Executes the Abstraction
///
/// \param V value to abstract
///
/// \return the abstracted value
R operator()(const D &V) const noexcept override {
R ret = 0;
if (V <= 0)
ret = 0;
else if (V >= RightLimit)
ret = 1;
else
ret = V * Coefficient;
return Direction == StepDirection::StepUp ? ret : 1 - ret;
}
};
/// Implements \c rosa::agent::Abstraction as a partial function from a domain
/// to a range.
///
/// \note This implementation is supposed to be used to represent a partial
/// function from an arithmetic domain to an arithmetic range. This is enforced
/// statically.
///
/// A partial function is defined as a list of abstractions, where each
/// abstraction is associated a range in which it is defined. These ranges must
/// be mutually exclusive.
///
/// \tparam D type of the functions domain
/// \tparam R type of the functions range
template <typename D, typename R>
class PartialFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value), "abstracting not arithmetic");
STATIC_ASSERT((std::is_arithmetic<R>::value),
"abstracting not to arithmetic");
private:
/// A \c rosa::agent::RangeAbstraction RA is used to represent the association
/// from ranges to Abstractions.
/// This returns the Abstraction that is defined for any given value, or
/// a default Abstraction if no Abstraction is defined for that value.
RangeAbstraction<D, std::shared_ptr<Abstraction<D, R>>> RA;
public:
/// Creates an instance by Initializing the underlying \c Abstraction.
///
/// \param Map the mapping to do abstraction according to
/// \param Default abstraction to abstract to by default
///
/// \pre Each key defines a valid range such that `first <= second` and
/// there are no overlapping ranges defined by the keys.
PartialFunction(
const std::map<std::pair<D, D>, std::shared_ptr<Abstraction<D, R>>> &Map,
const R Default)
: Abstraction<D, R>(Default),
RA(Map,
std::shared_ptr<Abstraction<D, R>>(new Abstraction<D, R>(Default))) {
}
/// Destroys \p this object.
~PartialFunction(void) = default;
/// Checks wether the Abstraction evaluates to default at the given position
///
/// \param V the value at which to check if the function falls back to it's
/// default value.
///
/// \return false if the value falls into a defined range and the Abstraction
/// defined for that range does not fall back to it's default value.
bool isDefaultAt(const D &V) const noexcept override {
return RA.isDefaultAt(V) ? true : RA(V)->isDefaultAt(V);
}
/// Searches for an Abstraction for the given value and executes it for that
/// value, if such an Abstraction is found. The default Abstraction is
/// evaluated otherwise.
///
/// \param V value to abstract
///
/// \return the abstracted value based on the set mapping
R operator()(const D &V) const noexcept override {
return RA(V)->operator()(V);
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP
diff --git a/include/rosa/agent/SignalState.hpp b/include/rosa/agent/SignalState.hpp
index 7557729..b6149a4 100644
--- a/include/rosa/agent/SignalState.hpp
+++ b/include/rosa/agent/SignalState.hpp
@@ -1,528 +1,470 @@
//===-- rosa/agent/SignalState.hpp ------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SignalState.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *signal state* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_SIGNALSTATE_HPP
#define ROSA_AGENT_SIGNALSTATE_HPP
#include "rosa/agent/FunctionAbstractions.hpp"
#include "rosa/agent/Functionality.h"
#include "rosa/agent/History.hpp"
#include "rosa/support/math.hpp"
namespace rosa {
namespace agent {
+/// Signal properties defining the properties of the signal which is monitored
+/// by \c rosa::agent::SignalStateDetector and is saved in \c
+/// rosa::agent::SignalStateInformation.
+enum SignalProperties : uint8_t {
+ INPUT = 0, ///< The signal is an input signal
+ OUTPUT = 1 ///< The signal is an output signal
+};
+
/// Signal state conditions defining how the condition of a \c
/// rosa::agent::SignalState is saved in \c rosa::agent::SignalStateInformation.
enum SignalStateCondition : uint8_t {
STABLE = 0, ///< The signal state is stable
DRIFTING = 1, ///< The signal state is drifting
UNKNOWN = 2 ///< The signal state is unknown
};
/// TODO: write description
template <typename CONFDATATYPE> struct SignalStateInformation {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
"confidence type is not to arithmetic");
/// The signal state ID saved as an uint32_teger number
uint32_t SignalStateID;
+ /// The SignalProperty saves whether the monitored signal is an input our
+ /// output signal.
+ SignalProperties SignalProperty;
/// The SignalStateConfidence shows the overall confidence value of the signal
/// state.
CONFDATATYPE SignalStateConfidence;
/// The SignalStateCondition shows the condition of a signal state (stable,
/// drifting, or unknown)
SignalStateCondition SignalStateCondition;
/// The SignalStateIsValid saves the number of samples which have been
/// inserted into the state after entering it.
uint32_t NumberOfInsertedSamplesAfterEntrance;
/// The SignalStateIsValid shows whether a signal state is valid or invalid.
/// In this context, valid means that enough samples which are in close
/// proximitry have been inserted into the signal state.
bool SignalStateIsValid;
/// The SignalStateJustGotValid shows whether a signal state got valid
/// (toggled from invalid to valid) during the current inserted sample.
bool SignalStateJustGotValid;
/// The SignalStateIsValidAfterReentrance shows whether a signal state is
/// valid after the variable changed back to it again.
bool SignalStateIsValidAfterReentrance;
- /// The SignalIsStableNotDrifting shows whether a signal is stable and not
- /// drifting.
- bool SignalIsStable;
};
/// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of
/// data in that the confidence values are given, \tparam PROCDATATYPE type of
/// the relative distance and the type of data in which DABs are saved.
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE>
class SignalState : public Functionality {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<INDATATYPE>::value),
"input data type not arithmetic");
STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
"confidence data type is not to arithmetic");
STATIC_ASSERT(
(std::is_arithmetic<PROCDATATYPE>::value),
"process data type (DAB and Relative Distance) is not to arithmetic");
-private:
+public:
// For the convinience to write a shorter data type name
- using PartFuncPointer =
- std::shared_ptr<PartialFunction<INDATATYPE, CONFDATATYPE>>;
- using StepFuncPointer =
- std::shared_ptr<StepFunction<INDATATYPE, CONFDATATYPE>>;
+ using PartFuncReference = PartialFunction<INDATATYPE, CONFDATATYPE> &;
+ using StepFuncReference = StepFunction<INDATATYPE, CONFDATATYPE> &;
+private:
/// SignalStateInfo is a struct SignalStateInformation that contains
/// information about the current state.
SignalStateInformation<CONFDATATYPE> SignalStateInfo;
/// The FuzzyFunctionSampleMatches is the fuzzy function that gives the
/// confidence how good the new sample matches another sample in the sample
/// history.
- PartFuncPointer FuzzyFunctionSampleMatches;
+ PartFuncReference FuzzyFunctionSampleMatches;
/// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the
/// confidence how bad the new sample matches another sample in the sample
/// history.
- PartFuncPointer FuzzyFunctionSampleMismatches;
+ PartFuncReference FuzzyFunctionSampleMismatches;
/// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history match the new sample.
- StepFuncPointer FuzzyFunctionNumOfSamplesMatches;
+ StepFuncReference FuzzyFunctionNumOfSamplesMatches;
/// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives
/// the confidence how many samples from the sampe history mismatch the new
/// sample.
- StepFuncPointer FuzzyFunctionNumOfSamplesMismatches;
+ StepFuncReference FuzzyFunctionNumOfSamplesMismatches;
/// The FuzzyFunctionSignalIsDrifting is the fuzzy function that gives the
/// confidence how likely it is that the signal (resp. the state of a signal)
/// is drifting.
- PartFuncPointer FuzzyFunctionSignalIsDrifting;
+ PartFuncReference FuzzyFunctionSignalIsDrifting;
/// The FuzzyFunctionSignalIsStable is the fuzzy function that gives the
/// confidence how likely it is that the signal (resp. the state of a signal)
/// is stable (not drifting).
- PartFuncPointer FuzzyFunctionSignalIsStable;
+ PartFuncReference FuzzyFunctionSignalIsStable;
/// SampleHistory is a history in that the last sample values are stored.
DynamicLengthHistory<INDATATYPE, HistoryPolicy::FIFO> SampleHistory;
/// DAB is a (usually) small history of the last sample values of which a
/// average is calculated if the DAB is full.
DynamicLengthHistory<INDATATYPE, HistoryPolicy::SRWF> DAB;
/// DABHistory is a history in that the last DABs (to be exact, the averages
/// of the last DABs) are stored.
DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::LIFO> DABHistory;
/// LowestConfidenceMatchingHistory is a history in that the lowest confidence
/// for the current sample matches all history samples are saved.
DynamicLengthHistory<INDATATYPE, HistoryPolicy::FIFO>
LowestConfidenceMatchingHistory;
/// HighestConfidenceMatchingHistory is a history in that the highest
/// confidence for the current sample matches all history samples are saved.
DynamicLengthHistory<INDATATYPE, HistoryPolicy::FIFO>
HighestConfidenceMismatchingHistory;
public:
- // @Maxi doxygen per default doesn't display private attributes of a class. So
- // I copied them to the constructor. So the user has more information.
/// Creates an instance by setting all parameters
/// \param SignalStateID The Id of the SignalStateinfo \c
/// SignalStateInformation.
///
/// \param FuzzyFunctionSampleMatches The FuzzyFunctionSampleMatches is the
/// fuzzy function that gives the confidence how good the new sample matches
/// another sample in the sample history.
///
/// \param FuzzyFunctionSampleMismatches The FuzzyFunctionSampleMismatches is
/// the fuzzy function that gives the confidence how bad the new sample
/// matches another sample in the sample history.
///
/// \param FuzzyFunctionNumOfSamplesMatches The
/// FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history match the new sample.
///
/// \param FuzzyFunctionNumOfSamplesMismatches The
/// FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history mismatch the new
/// sample.
///
/// \param FuzzyFunctionSignalIsDrifting The FuzzyFunctionSignalIsDrifting is
/// the fuzzy function that gives the confidence how likely it is that the
/// signal (resp. the state of a signal) is drifting.
///
/// \param FuzzyFunctionSignalIsStable The FuzzyFunctionSignalIsStable is the
/// fuzzy function that gives the confidence how likely it is that the signal
/// (resp. the state of a signal) is stable (not drifting).
///
/// \param SampleHistorySize Size of the Sample History \c
/// DynamicLengthHistory . SampleHistory is a history in that the last sample
/// values are stored.
///
/// \param DABSize Size of DAB \c DynamicLengthHistory . DAB is a (usually)
/// small history of the last sample values of which a average is calculated
/// if the DAB is full.
///
/// \param DABHistorySize Size of the DABHistory \c DynamicLengthHistory .
/// DABHistory is a history in that the last DABs (to be exact, the averages
/// of the last DABs) are stored.
///
- SignalState(uint32_t SignalStateID, uint32_t SampleHistorySize,
- uint32_t DABSize, uint32_t DABHistorySize,
- PartFuncPointer FuzzyFunctionSampleMatches,
- PartFuncPointer FuzzyFunctionSampleMismatches,
- StepFuncPointer FuzzyFunctionNumOfSamplesMatches,
- StepFuncPointer FuzzyFunctionNumOfSamplesMismatches,
- PartFuncPointer FuzzyFunctionSignalIsDrifting,
- PartFuncPointer FuzzyFunctionSignalIsStable) noexcept
- : SignalStateInfo{SignalStateID, 0, SignalStateCondition::UNKNOWN, 0,
- false, false,
- false, //@maxi added the Signal is stable bool
- true},
-
+ SignalState(uint32_t SignalStateID, SignalProperties SignalProperty,
+ uint32_t SampleHistorySize, uint32_t DABSize,
+ uint32_t DABHistorySize,
+ PartFuncReference FuzzyFunctionSampleMatches,
+ PartFuncReference FuzzyFunctionSampleMismatches,
+ StepFuncReference FuzzyFunctionNumOfSamplesMatches,
+ StepFuncReference FuzzyFunctionNumOfSamplesMismatches,
+ PartFuncReference FuzzyFunctionSignalIsDrifting,
+ PartFuncReference FuzzyFunctionSignalIsStable) noexcept
+ : SignalStateInfo{SignalStateID,
+ SignalProperty,
+ 0,
+ SignalStateCondition::UNKNOWN,
+ 0,
+ false,
+ false,
+ false},
FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches),
FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches),
FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches),
FuzzyFunctionNumOfSamplesMismatches(
FuzzyFunctionNumOfSamplesMismatches),
FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting),
FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable),
SampleHistory(SampleHistorySize), DAB(DABSize),
DABHistory(DABHistorySize),
LowestConfidenceMatchingHistory(SampleHistorySize),
HighestConfidenceMismatchingHistory(SampleHistorySize) {}
/// Destroys \p this object.
~SignalState(void) = default;
void leaveSignalState(void) noexcept {
DAB.clear();
SignalStateInfo.NumberOfInsertedSamplesAfterEntrance = 0;
SignalStateInfo.SignalStateIsValidAfterReentrance = false;
}
SignalStateInformation<CONFDATATYPE>
insertSample(INDATATYPE Sample) noexcept {
validateSignalState(Sample);
SampleHistory.addEntry(Sample);
DAB.addEntry(Sample);
if (DAB.full()) {
PROCDATATYPE AvgOfDAB = DAB.template average<PROCDATATYPE>();
DABHistory.addEntry(AvgOfDAB);
DAB.clear();
}
- FuzzyFunctionNumOfSamplesMatches->setRightLimit(
+ FuzzyFunctionNumOfSamplesMatches.setRightLimit(
static_cast<INDATATYPE>(SampleHistory.numberOfEntries()));
- FuzzyFunctionNumOfSamplesMismatches->setRightLimit(
+ FuzzyFunctionNumOfSamplesMismatches.setRightLimit(
static_cast<INDATATYPE>(SampleHistory.numberOfEntries()));
checkSignalStability();
return SignalStateInfo;
}
/// Gives the confidence how likely the new sample matches the signal state.
///
/// \param Sample is the actual sample of the observed signal.
///
/// \return the confidence of the new sample is matching the signal state.
CONFDATATYPE
confidenceSampleMatchesSignalState(INDATATYPE Sample) noexcept {
CONFDATATYPE ConfidenceOfBestCase = 0;
DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::FIFO>
RelativeDistanceHistory(SampleHistory.maxLength());
// calculate distances to all history samples
for (auto &HistorySample : SampleHistory) {
PROCDATATYPE RelativeDistance =
relativeDistance<INDATATYPE, PROCDATATYPE>(Sample, HistorySample);
RelativeDistanceHistory.addEntry(RelativeDistance);
}
// sort all calculated distances so that the lowest distance (will get the
// highest confidence) is at the beginning.
RelativeDistanceHistory.sortAscending();
CONFDATATYPE ConfidenceOfWorstFittingSample = 1;
// Case 1 means that one (the best fitting) sample of the history is
// compared with the new sample. Case 2 means the two best history samples
// are compared with the new sample. And so on.
- // TODO (future): to accelerate -> don't start with 1 start with some higher
+ // TODO (future): to accelerate . don't start with 1 start with some higher
// number because a low number (i guess lower than 5) will definetely lead
// to a low confidence. except the history is not full.
-
+ //
+ // Case 1 means that one (the best fitting) sample of the history is
+ // compared with the new sample. Case 2 means the two best history samples
+ // are compared with the new sample. And so on.
for (uint32_t Case = 0; Case < RelativeDistanceHistory.numberOfEntries();
Case++) {
CONFDATATYPE ConfidenceFromRelativeDistance;
if (std::isinf(RelativeDistanceHistory[Case])) {
// TODO (future) if fuzzy is defined in a way that infinity is not 0 it
// would be a problem
- //@David: because we are using these operator functions, here I have to
- // direference in a not (in my opionin) not so beautiful way
- //("(*FuzzyFunctionSampleMatches)"), or I have to write
- //"FuzzyFunctionSampleMatches->operator()(...)". Can we just write
- // functions like "->getBlabla()" or something like that?
ConfidenceFromRelativeDistance = 0;
} else {
ConfidenceFromRelativeDistance =
- (*FuzzyFunctionSampleMatches)(RelativeDistanceHistory[Case]);
+ FuzzyFunctionSampleMatches(RelativeDistanceHistory[Case]);
}
-#if false
- //@maxi why the 2?
- ConfidenceOfWorstFittingSample =
- fuzzyAND((CONFDATATYPE)2, ConfidenceOfWorstFittingSample,
- ConfidenceFromRelativeDistance);
-
- //@maxi you could also use it like this then you can define the type and the size
- ConfidenceOfWorstFittingSample =
- fuzzyAND<CONFDATATYPE,2>({ConfidenceOfWorstFittingSample,
- ConfidenceFromRelativeDistance});
-#else
- //@maxi is this what you wanted? you don't need to define the data type
- // nor the size
ConfidenceOfWorstFittingSample = fuzzyAND(ConfidenceOfWorstFittingSample,
ConfidenceFromRelativeDistance);
-#endif
-
- //@benedikt: same as before with "->operator()"
- //@maxi the same as before
-#if false
- ConfidenceOfBestCase = fuzzyOR<CONFDATATYPE>(
- 2, ConfidenceOfBestCase,
- fuzzyAND<CONFDATATYPE>(2, ConfidenceOfWorstFittingSample,
- FuzzyFunctionNumOfSamplesMatches->operator()(
- static_cast<CONFDATATYPE>(Case) + 1)));
-#else
+
ConfidenceOfBestCase =
fuzzyOR(ConfidenceOfBestCase,
fuzzyAND(ConfidenceOfWorstFittingSample,
- FuzzyFunctionNumOfSamplesMatches->operator()(
+ FuzzyFunctionNumOfSamplesMatches(
static_cast<CONFDATATYPE>(Case) + 1)));
-
-#endif
}
return ConfidenceOfBestCase;
}
/// Gives the confidence how likely the new sample mismatches the signal
/// state.
///
/// \param Sample is the actual sample of the observed signal.
///
/// \return the confidence of the new sample is mismatching the signal state.
CONFDATATYPE
confidenceSampleMismatchesSignalState(INDATATYPE Sample) noexcept {
float ConfidenceOfWorstCase = 1;
DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::FIFO>
RelativeDistanceHistory(SampleHistory.maxLength());
// calculate distances to all history samples
for (auto &HistorySample : SampleHistory) {
RelativeDistanceHistory.addEntry(
relativeDistance<INDATATYPE, PROCDATATYPE>(Sample, HistorySample));
}
// sort all calculated distances so that the highest distance (will get the
// lowest confidence) is at the beginning.
RelativeDistanceHistory.sortDescending();
CONFDATATYPE ConfidenceOfBestFittingSample = 0;
- // Case 1 means that one (the worst fitting) sample of the history is
- // compared with the new sample. Case 2 means the two worst history samples
- // are compared with the new sample. And so on.
// TODO (future): to accelerate -> don't go until end. Confidences will only
// get higher. See comment in "CONFDATATYPE
// confidenceSampleMatchesSignalState(INDATATYPE Sample)".
+ //
+ // Case 1 means that one (the worst fitting) sample of the history is
+ // compared with the new sample. Case 2 means the two worst history samples
+ // are compared with the new sample. And so on.
for (uint32_t Case = 0; Case < RelativeDistanceHistory.numberOfEntries();
Case++) {
CONFDATATYPE ConfidenceFromRelativeDistance;
if (std::isinf(RelativeDistanceHistory[Case])) {
ConfidenceFromRelativeDistance = 1;
} else {
- //@benedikt: I had to change the following line. The outcommented line
- // was the original one. I think it is ugly like that (new line). Do you
- // have an idea how to make it better/more beautiful?
ConfidenceFromRelativeDistance =
- FuzzyFunctionSampleMismatches->operator()(
- RelativeDistanceHistory[Case]);
+ FuzzyFunctionSampleMismatches(RelativeDistanceHistory[Case]);
}
-//@maxi you don't have to define the data type or the amount
-#if false
- ConfidenceOfBestFittingSample = fuzzyOR<CONFDATATYPE>(
- 2, ConfidenceOfBestFittingSample, ConfidenceFromRelativeDistance);
-
- //@benedikt: same as before with "->operator()"
- ConfidenceOfWorstCase = fuzzyAND<CONFDATATYPE>(
- 2, ConfidenceOfWorstCase,
- fuzzyOR<CONFDATATYPE>(2, ConfidenceOfBestFittingSample,
- FuzzyFunctionNumOfSamplesMismatches->operator()(
- static_cast<CONFDATATYPE>(Case) + 1)));
-#else
ConfidenceOfBestFittingSample = fuzzyOR(ConfidenceOfBestFittingSample,
ConfidenceFromRelativeDistance);
- //@benedikt: same as before with "->operator()"
ConfidenceOfWorstCase =
fuzzyAND(ConfidenceOfWorstCase,
fuzzyOR(ConfidenceOfBestFittingSample,
- FuzzyFunctionNumOfSamplesMismatches->operator()(
+ FuzzyFunctionNumOfSamplesMismatches(
static_cast<CONFDATATYPE>(Case) + 1)));
-#endif
}
return ConfidenceOfWorstCase;
}
/// Gives information about the current signal state.
///
/// \return a struct SignalStateInformation that contains information about
/// the current signal state.
SignalStateInformation<CONFDATATYPE> signalStateInformation(void) noexcept {
return SignalStateInfo;
}
private:
void validateSignalState(INDATATYPE Sample) {
// TODO (future): WorstConfidenceDistance and BestConfidenceDistance could
// be set already in "CONFDATATYPE
// confidenceSampleMatchesSignalState(INDATATYPE Sample)" and "CONFDATATYPE
// confidenceSampleMismatchesSignalState(INDATATYPE Sample)" when the new
// sample is compared to all history samples. This would save a lot time
// because the comparisons are done only once. However, it has to be asured
// that the these two functions are called before the insertation, and the
// FuzzyFunctions for validation and matching have to be the same!
CONFDATATYPE LowestConfidenceMatching = 1;
CONFDATATYPE HighestConfidenceMismatching = 0;
for (auto &HistorySample : SampleHistory) {
// TODO (future): think about using different fuzzy functions for
// validation and matching.
- //@benedikt: same with "->operator()"
- //@maxi you don't have to deine the data type nor the amount
-#if false
- LowestConfidenceMatching =
- fuzzyAND<CONFDATATYPE>(2, LowestConfidenceMatching,
- FuzzyFunctionSampleMatches->operator()(
- relativeDistance<INDATATYPE, PROCDATATYPE>(
- Sample, HistorySample)));
- //@benedikt: same with "->operator()"
- HighestConfidenceMismatching =
- fuzzyOR<CONFDATATYPE>(2, HighestConfidenceMismatching,
- FuzzyFunctionSampleMismatches->operator()(
- relativeDistance<INDATATYPE, PROCDATATYPE>(
- Sample, HistorySample)));
-#else
- LowestConfidenceMatching =
- fuzzyAND(LowestConfidenceMatching,
- FuzzyFunctionSampleMatches->operator()(
- relativeDistance<INDATATYPE, PROCDATATYPE>(
- Sample, HistorySample)));
- //@benedikt: same with "->operator()"
+ LowestConfidenceMatching = fuzzyAND(
+ LowestConfidenceMatching,
+ FuzzyFunctionSampleMatches(relativeDistance<INDATATYPE, PROCDATATYPE>(
+ Sample, HistorySample)));
+
HighestConfidenceMismatching =
fuzzyOR(HighestConfidenceMismatching,
- FuzzyFunctionSampleMismatches->operator()(
+ FuzzyFunctionSampleMismatches(
relativeDistance<INDATATYPE, PROCDATATYPE>(
Sample, HistorySample)));
-#endif
}
LowestConfidenceMatchingHistory.addEntry(LowestConfidenceMatching);
HighestConfidenceMismatchingHistory.addEntry(HighestConfidenceMismatching);
LowestConfidenceMatching = LowestConfidenceMatchingHistory.lowestEntry();
HighestConfidenceMismatching =
HighestConfidenceMismatchingHistory.highestEntry();
- //@maxi you don't have to define the data type or the amount
-#if false
- //@benedikt: same with "->operator()"
- CONFDATATYPE ConfidenceSignalStateIsValid = fuzzyAND<CONFDATATYPE>(
- 2, LowestConfidenceMatching,
- FuzzyFunctionNumOfSamplesMatches->operator()(static_cast<INDATATYPE>(
- SignalStateInfo.NumberOfInsertedSamplesAfterEntrance)));
- //@benedikt: same with "->operator()"
- CONFDATATYPE ConfidenceSignalStateIsInvalid = fuzzyOR<CONFDATATYPE>(
- 2, HighestConfidenceMismatching,
- FuzzyFunctionNumOfSamplesMismatches->operator()(static_cast<INDATATYPE>(
- SignalStateInfo.NumberOfInsertedSamplesAfterEntrance)));
-#else
- //@benedikt: same with "->operator()"
- CONFDATATYPE ConfidenceSignalStateIsValid = fuzzyAND(
- LowestConfidenceMatching,
- FuzzyFunctionNumOfSamplesMatches->operator()(static_cast<INDATATYPE>(
- SignalStateInfo.NumberOfInsertedSamplesAfterEntrance)));
- //@benedikt: same with "->operator()"
- CONFDATATYPE ConfidenceSignalStateIsInvalid = fuzzyOR(
- HighestConfidenceMismatching,
- FuzzyFunctionNumOfSamplesMismatches->operator()(static_cast<INDATATYPE>(
- SignalStateInfo.NumberOfInsertedSamplesAfterEntrance)));
-#endif
+ CONFDATATYPE ConfidenceSignalStateIsValid =
+ fuzzyAND(LowestConfidenceMatching,
+ FuzzyFunctionNumOfSamplesMatches(static_cast<INDATATYPE>(
+ SignalStateInfo.NumberOfInsertedSamplesAfterEntrance)));
+
+ CONFDATATYPE ConfidenceSignalStateIsInvalid =
+ fuzzyOR(HighestConfidenceMismatching,
+ FuzzyFunctionNumOfSamplesMismatches(static_cast<INDATATYPE>(
+ SignalStateInfo.NumberOfInsertedSamplesAfterEntrance)));
if (ConfidenceSignalStateIsValid > ConfidenceSignalStateIsInvalid) {
if (SignalStateInfo.SignalStateIsValid) {
SignalStateInfo.SignalStateJustGotValid = false;
} else {
SignalStateInfo.SignalStateJustGotValid = true;
}
SignalStateInfo.SignalStateIsValid = true;
SignalStateInfo.SignalStateIsValidAfterReentrance = true;
}
}
void checkSignalStability(void) {
CONFDATATYPE ConfidenceSignalIsStable;
CONFDATATYPE ConfidenceSignalIsDrifting;
if (DABHistory.numberOfEntries() >= 2) {
- //@benedikt: same "->operator()"
- ConfidenceSignalIsStable = FuzzyFunctionSignalIsStable->operator()(
+ ConfidenceSignalIsStable = FuzzyFunctionSignalIsStable(
relativeDistance<INDATATYPE, PROCDATATYPE>(
DABHistory[DABHistory.numberOfEntries() - 1], DABHistory[0]));
- //@benedikt: same "->operator()"
- ConfidenceSignalIsDrifting = FuzzyFunctionSignalIsDrifting->operator()(
+ ConfidenceSignalIsDrifting = FuzzyFunctionSignalIsDrifting(
relativeDistance<INDATATYPE, PROCDATATYPE>(
DABHistory[DABHistory.numberOfEntries() - 1], DABHistory[0]));
} else {
- // TODO: change to enum
- ConfidenceSignalIsStable = 1;
+ // @benedikt: I do not know if this "initializing" is the best, but I
+ // think it makes sense because we do not know if it is stable or
+ // drifting.
+ ConfidenceSignalIsStable = 0;
ConfidenceSignalIsDrifting = 0;
}
- SignalStateInfo.SignalIsStable =
- ConfidenceSignalIsStable >= ConfidenceSignalIsDrifting;
+ //@benedikt: before it was "ConfidenceSignalIsStable >=
+ // ConfidenceSignalIsDrifting" -> stable. However, I think like that it
+ // makes
+ // more sense. What do you mean?
+ if (ConfidenceSignalIsStable > ConfidenceSignalIsDrifting) {
+ SignalStateInfo.SignalStateCondition = SignalStateCondition::STABLE;
+ } else if (ConfidenceSignalIsStable < ConfidenceSignalIsDrifting) {
+ SignalStateInfo.SignalStateCondition = SignalStateCondition::DRIFTING;
+ } else {
+ SignalStateInfo.SignalStateCondition = SignalStateCondition::UNKNOWN;
+ }
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SIGNALSTATE_HPP
diff --git a/include/rosa/agent/SignalStateDetector.hpp b/include/rosa/agent/SignalStateDetector.hpp
index d7b10e9..207a094 100644
--- a/include/rosa/agent/SignalStateDetector.hpp
+++ b/include/rosa/agent/SignalStateDetector.hpp
@@ -1,278 +1,285 @@
//===-- rosa/agent/SignalStateDetector.hpp ----------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SignalStateDetector.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *signal state detector* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_SIGNALSTATEDETECTOR_HPP
#define ROSA_AGENT_SIGNALSTATEDETECTOR_HPP
#include "rosa/agent/Functionality.h"
#include "rosa/agent/SignalState.hpp"
#include "rosa/agent/StateDetector.hpp"
#include <vector>
namespace rosa {
namespace agent {
/// Implements \c rosa::agent::SignalStateDetector as a functionality that
/// detects signal states given on input samples.
///
/// \note This implementation is supposed to be used for samples of an
/// arithmetic type.
///
/// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of
/// data in that the confidence values are given, \tparam PROCDATATYPE type of
/// the relative distance and the type of data in which DABs are saved.
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE,
HistoryPolicy HP>
class SignalStateDetector
: public StateDetector<INDATATYPE, CONFDATATYPE, PROCDATATYPE, HP> {
- // @maxi added them so it is compilable is this what you intended?
+ // @maxi added them so it is compilable is this what you intended?
using StateDetector =
StateDetector<INDATATYPE, CONFDATATYPE, PROCDATATYPE, HP>;
using PartFuncPointer = typename StateDetector::PartFuncPointer;
using StepFuncPointer = typename StateDetector::StepFuncPointer;
private:
// For the convinience to write a shorter data type name
using SignalStatePtr =
std::shared_ptr<SignalState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>>;
+ /// The SignalProperty saves whether the monitored signal is an input our
+ /// output signal.
+ SignalProperties SignalProperty;
+
/// The NextSignalStateID is a counter variable which stores the ID which the
/// next signal state shall have.
uint32_t NextSignalStateID;
/// The SignalStateHasChanged is a flag that show whether a signal has changed
/// its state.
bool SignalStateHasChanged;
/// The CurrentSignalState is a pointer to the (saved) signal state in which
/// the actual variable (signal) of the observed system is.
SignalStatePtr CurrentSignalState;
/// The DetectedSignalStates is a history in that all detected signal states
/// are saved.
DynamicLengthHistory<SignalStatePtr, HP> DetectedSignalStates;
/// The FuzzyFunctionSampleMatches is the fuzzy function that gives the
/// confidence how good the new sample matches another sample in the sample
/// history.
PartFuncPointer FuzzyFunctionSampleMatches;
/// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the
/// confidence how bad the new sample matches another sample in the sample
/// history.
PartFuncPointer FuzzyFunctionSampleMismatches;
/// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history match the new sample.
StepFuncPointer FuzzyFunctionNumOfSamplesMatches;
/// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives
/// the confidence how many samples from the sampe history mismatch the new
/// sample.
StepFuncPointer FuzzyFunctionNumOfSamplesMismatches;
/// The FuzzyFunctionSignalIsDrifting is the fuzzy function that gives the
/// confidence how likely it is that the signal is drifting.
PartFuncPointer FuzzyFunctionSignalIsDrifting;
/// The FuzzyFunctionSignalIsStable is the fuzzy function that gives the
/// confidence how likely it is that the signal is stable (not drifting).
PartFuncPointer FuzzyFunctionSignalIsStable;
/// SampleHistorySize is the (maximum) size of the sample history.
uint32_t SampleHistorySize;
/// DABSize the size of a DAB (Discrete Average Block).
uint32_t DABSize;
/// DABHistorySize is the (maximum) size of the DAB history.
uint32_t DABHistorySize;
public:
/// Creates an instance by setting all parameters
/// \param FuzzyFunctionSampleMatches The FuzzyFunctionSampleMatches is the
/// fuzzy function that gives the confidence how good the new sample matches
/// another sample in the sample history.
///
/// \param FuzzyFunctionSampleMismatches The FuzzyFunctionSampleMismatches is
/// the fuzzy function that gives the confidence how bad the new sample
/// matches another sample in the sample history.
///
/// \param FuzzyFunctionNumOfSamplesMatches The
/// FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history match the new sample.
///
/// \param FuzzyFunctionNumOfSamplesMismatches The
/// FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history mismatch the new
/// sample.
///
/// \param FuzzyFunctionSignalIsDrifting The FuzzyFunctionSignalIsDrifting is
/// the fuzzy function that gives the confidence how likely it is that the
/// signal (resp. the state of a signal) is drifting.
///
/// \param FuzzyFunctionSignalIsStable The FuzzyFunctionSignalIsStable is the
/// fuzzy function that gives the confidence how likely it is that the signal
/// (resp. the state of a signal) is stable (not drifting).
///
/// \param SampleHistorySize Sets the History size which will be used by \c
/// SignalState.
///
/// \param DABSize Sets the DAB size which will be used by \c SignalState.
///
/// \param DABHistorySize Sets the size which will be used by \c SignalState.
///
- SignalStateDetector(uint32_t MaximumNumberOfSignalStates,
+ SignalStateDetector(SignalProperties SignalProperty,
+ uint32_t MaximumNumberOfSignalStates,
PartFuncPointer FuzzyFunctionSampleMatches,
PartFuncPointer FuzzyFunctionSampleMismatches,
StepFuncPointer FuzzyFunctionNumOfSamplesMatches,
StepFuncPointer FuzzyFunctionNumOfSamplesMismatches,
PartFuncPointer FuzzyFunctionSignalIsDrifting,
PartFuncPointer FuzzyFunctionSignalIsStable,
uint32_t SampleHistorySize, uint32_t DABSize,
uint32_t DABHistorySize) noexcept
- : NextSignalStateID(1), SignalStateHasChanged(false),
- CurrentSignalState(nullptr),
+ : // needed to be reorderd
+ SignalProperty(SignalProperty), NextSignalStateID(1),
+ SignalStateHasChanged(false), CurrentSignalState(nullptr),
DetectedSignalStates(MaximumNumberOfSignalStates),
FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches),
FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches),
FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches),
FuzzyFunctionNumOfSamplesMismatches(
FuzzyFunctionNumOfSamplesMismatches),
FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting),
FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable),
SampleHistorySize(SampleHistorySize), DABSize(DABSize),
DABHistorySize(DABHistorySize) {}
/// Destroys \p this object.
~SignalStateDetector(void) = default;
/// Detects the signal state to which the new sample belongs or create a new
/// signal state if the new sample does not match to any of the saved states.
///
/// \param Sample is the actual sample of the observed signal.
///
/// \return the information of the current signal state (signal state ID and
/// other parameters).
// TODO (future): change to operator()
SignalStateInformation<CONFDATATYPE>
detectSignalState(INDATATYPE Sample) noexcept {
if (!CurrentSignalState) {
ASSERT(DetectedSignalStates.empty());
SignalStatePtr S = createNewSignalState();
CurrentSignalState = S;
} else {
CONFDATATYPE ConfidenceSampleMatchesSignalState =
CurrentSignalState->confidenceSampleMatchesSignalState(Sample);
CONFDATATYPE ConfidenceSampleMismatchesSignalState =
CurrentSignalState->confidenceSampleMismatchesSignalState(Sample);
if (ConfidenceSampleMatchesSignalState >
ConfidenceSampleMismatchesSignalState) {
SignalStateHasChanged = false;
} else {
SignalStateHasChanged = true;
if (CurrentSignalState->signalStateInformation().SignalStateIsValid) {
CurrentSignalState->leaveSignalState();
} else {
DetectedSignalStates.deleteEntry(CurrentSignalState);
}
// TODO (future): additionally save averages to enable fast iteration
// through recorded signl state history (maybe sort vector based on
// these average values)
CurrentSignalState = nullptr;
//@benedikt: same question
for (auto &SavedSignalState : DetectedSignalStates) {
if (SavedSignalState != CurrentSignalState) {
ConfidenceSampleMatchesSignalState =
SavedSignalState->confidenceSampleMatchesSignalState(Sample);
ConfidenceSampleMismatchesSignalState =
SavedSignalState->confidenceSampleMismatchesSignalState(Sample);
if (ConfidenceSampleMatchesSignalState >
ConfidenceSampleMismatchesSignalState) {
// TODO (future): maybe it would be better to compare
// ConfidenceSampleMatchesSignalState of all signal states in the
// vector in order to find the best matching signal state.
CurrentSignalState = SavedSignalState;
break;
}
}
}
if (!CurrentSignalState) {
SignalStatePtr S = createNewSignalState();
CurrentSignalState = S;
}
}
}
SignalStateInformation<CONFDATATYPE> SignalStateInfo =
CurrentSignalState->insertSample(Sample);
if (SignalStateInfo.SignalStateJustGotValid) {
NextSignalStateID++;
}
return SignalStateInfo;
}
/// Gives information about the current signal state.
///
/// \return a struct SignalStateInformation that contains information about
/// the current signal state or NULL if no current signal state exists.
SignalStateInformation<CONFDATATYPE>
currentSignalStateInformation(void) noexcept {
if (CurrentSignalState) {
return CurrentSignalState->signalStateInformation();
} else {
return NULL;
}
}
/// Gives information whether a signal state change has happened or not.
///
/// \return true if a signal state change has happened, and false if not.
bool signalStateHasChanged(void) noexcept { return SignalStateHasChanged; }
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.
SignalStatePtr createNewSignalState(void) noexcept {
SignalStatePtr S(new SignalState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>(
- NextSignalStateID, SampleHistorySize, DABSize, DABHistorySize,
- FuzzyFunctionSampleMatches, FuzzyFunctionSampleMismatches,
- FuzzyFunctionNumOfSamplesMatches, FuzzyFunctionNumOfSamplesMismatches,
- FuzzyFunctionSignalIsDrifting, FuzzyFunctionSignalIsStable));
+ NextSignalStateID, SignalProperty, SampleHistorySize, DABSize,
+ DABHistorySize, *FuzzyFunctionSampleMatches,
+ *FuzzyFunctionSampleMismatches, *FuzzyFunctionNumOfSamplesMatches,
+ *FuzzyFunctionNumOfSamplesMismatches, *FuzzyFunctionSignalIsDrifting,
+ *FuzzyFunctionSignalIsStable));
DetectedSignalStates.addEntry(S);
return S;
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SIGNALSTATEDETECTOR_HPP
diff --git a/include/rosa/agent/StateDetector.hpp b/include/rosa/agent/StateDetector.hpp
index 9bd7430..aa341f6 100644
--- a/include/rosa/agent/StateDetector.hpp
+++ b/include/rosa/agent/StateDetector.hpp
@@ -1,57 +1,56 @@
//===-- rosa/agent/StateDetector.hpp ----------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/StateDetector.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *state detector* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_STATEDETECTOR_HPP
#define ROSA_AGENT_STATEDETECTOR_HPP
#include "rosa/agent/FunctionAbstractions.hpp"
#include "rosa/agent/History.hpp"
#include <vector>
namespace rosa {
namespace agent {
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE,
HistoryPolicy HP>
class StateDetector : public Functionality {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<INDATATYPE>::value),
"input data type not arithmetic");
STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
"confidence abstraction type is not to arithmetic");
-public:
+protected:
using PartFuncPointer =
std::shared_ptr<PartialFunction<INDATATYPE, CONFDATATYPE>>;
using StepFuncPointer =
std::shared_ptr<StepFunction<INDATATYPE, CONFDATATYPE>>;
-protected:
/// The NextSignalStateID is a counter variable which stores the ID which the
/// next signal state shall have.
unsigned int NextStateID;
/// The SignalStateHasChanged is a flag that show whether a signal has changed
/// its state.
bool StateHasChanged;
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SIGNALSTATEDETECTOR_HPP
diff --git a/include/rosa/agent/SystemState.hpp b/include/rosa/agent/SystemState.hpp
index 333681f..e1fa090 100644
--- a/include/rosa/agent/SystemState.hpp
+++ b/include/rosa/agent/SystemState.hpp
@@ -1,55 +1,156 @@
//===-- 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 <array>
+#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,
- std::size_t NUMOFINPUTSIGNALS, std::size_t NUMOFOUTPUTSIGNALS>
+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:
- // TODO: vector
- std::array<SignalState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>,
- NUMOFINPUTSIGNALS>
- InputSignalStates;
- std::array<SignalState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>,
- NUMOFOUTPUTSIGNALS>
- OutputSignalStates;
+ SystemStateInformation<CONFDATATYPE> SystemStateInfo;
+
+ std::vector<SignalStateInformation<CONFDATATYPE>> Signals;
+
+ uint32_t NumberOfSignals;
public:
- // SystemState(unsigned int NumberOfInputSignals) noexcept : {}
+ /// TODO write
+ SystemState(uint32_t SignalStateID, uint32_t NumberOfSignals) noexcept
+ : SystemStateInfo{SignalStateID,
+ 0,
+ SystemStateCondition::UNKNOWN,
+ 0,
+ false,
+ false,
+ false},
+ NumberOfSignals(NumberOfSignals) {
+ Signals.resize(NumberOfSignals);
+ }
+
+ /// Destroys \p this object.
+ ~SystemState(void) = default;
+
+ template <std::size_t size>
+ void
+ insertSignalStateInfos(const std::array<SystemStateInformation<CONFDATATYPE>,
+ size> &Data) noexcept {
+ ASSERT(size <= NumberOfSignals);
+ std::size_t counter = 0;
+ for (auto tmp : Data) {
+ Signals.at(counter) = tmp;
+ counter++;
+ }
+ }
+
+ template <typename... Types>
+ std::enable_if_t<std::conjunction_v<std::is_same<
+ Types, SystemStateInformation<CONFDATATYPE>>...>,
+ void>
+ insertSignalStateInfos(Types... Data) {
+ insertSignalStateInfos(
+ std::array<SystemStateInformation<CONFDATATYPE>, sizeof...(Data)>(
+ {Data...}));
+ }
+
+ // returns true if they are identical
+ template <std::size_t size>
+ bool
+ compareSignalStateInfos(const std::array<SystemStateInformation<CONFDATATYPE>,
+ size> &Data) noexcept {
+ std::size_t counter = 0;
+ for (auto tmp : Data) {
+ if (Signals.at(counter) != tmp)
+ return false;
+ counter++;
+ }
+ return true;
+ }
+
+ // checks only the given amount
+ template <typename... Types>
+ std::enable_if_t<std::conjunction_v<std::is_same<
+ Types, SystemStateInformation<CONFDATATYPE>>...>,
+ bool>
+ compareSignalStateInfos(Types... Data) {
+ 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 fdd600c..d18607c 100644
--- a/include/rosa/agent/SystemStateDetector.hpp
+++ b/include/rosa/agent/SystemStateDetector.hpp
@@ -1,146 +1,104 @@
//===-- 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 {
-/// 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)
- //@David: is it ok to name the variable exactly as the type is named?
- 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;
- /// The SystemIsStable shows whether a signa is stable and not
- /// drifting.
- bool SystemIsStable;
-};
-
+// todo: löschen , std::size_t NUMOFINPUTSIGNALS, std::size_t NUMOFOUTPUTSIGNALS
/// TODO: write description
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE,
- HistoryPolicy HP, std::size_t NUMOFINPUTSIGNALS,
- std::size_t NUMOFOUTPUTSIGNALS>
+ 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,
- NUMOFINPUTSIGNALS, NUMOFOUTPUTSIGNALS>>;
+ 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),
DetectedSystemStates(MaximumNumberOfSystemStates),
FuzzyFunctionDelayTimeToGetBroken(FuzzyFunctionDelayTimeToGetBroken),
FuzzyFunctionDelayTimeToBeWorking(FuzzyFunctionDelayTimeToBeWorking) {}
/// Destroys \p this object.
~SystemStateDetector(void) = default;
/// TODO: write description
SystemStateInformation<CONFDATATYPE>
detectSystemState(INDATATYPE Sample) noexcept {
// dummy line
Sample = 1;
}
};
} // 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 3fde782..fa328a2 100644
--- a/include/rosa/deluxe/DeluxeAgent.hpp
+++ b/include/rosa/deluxe/DeluxeAgent.hpp
@@ -1,1453 +1,1456 @@
//===-- 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
/// 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...> {
/// 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 {
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)));
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>());
}
} 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 successfuuly constructed.
- ASSERT((true && ... &&
- (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ // 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 successfuuly constructed.
- ASSERT((true && ... &&
- (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ // 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 successfuuly constructed.
- ASSERT((true && ... &&
- (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ // 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/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp
index 95bb94d..5b53835 100644
--- a/include/rosa/deluxe/DeluxeSensor.hpp
+++ b/include/rosa/deluxe/DeluxeSensor.hpp
@@ -1,668 +1,672 @@
//===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeSensor.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe
/// interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_DELUXE_DELUXESENSOR_HPP
#define ROSA_DELUXE_DELUXESENSOR_HPP
#include "rosa/core/Agent.hpp"
#include "rosa/deluxe/DeluxeAtoms.hpp"
#include "rosa/deluxe/DeluxeExecutionPolicy.h"
#include "rosa/deluxe/DeluxeTuple.hpp"
/// Local helper macros to deal with built-in types.
///
///@{
/// Creates function name for member functions in \c rosa::deluxe::DeluxeSensor.
///
/// \param N name suffix to use
#define DSMASTERHANDLERNAME(N) handleMaster_##N
/// Defines member functions for handling messages from *master* in
/// \c rosa::deluxe::DeluxeSensor.
///
/// \see \c DeluxeSensorMasterInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeSensor::saveMasterInput to do
/// that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DSMASTERHANDLERDEFN(T, N) \
void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \
T Value) noexcept { \
saveMasterInput(MasterId, Pos, Value); \
}
/// Convenience macro for \c DSMASTERHANDLERDEFN with identical arguments.
///
/// \see \c DSMASTERHANDLERDEFN
///
/// This macro can be used instead of \c DSMASTERHANDLERDEFN if the actual value
/// of \p T can be used as a part of a valid identifier.
///
/// \param T the type of input to handle
#define DSMASTERHANDLERDEF(T) DSMASTERHANDLERDEFN(T, T)
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DSMASTERHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeSensor to initialize super
/// class \c rosa::Agent with member function defined by \c DSMASTERHANDLERDEFN.
///
/// \see \c DSMASTERHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DSMASTERHANDLERREF(N) THISMEMBER(DSMASTERHANDLERNAME(N))
///@}
namespace rosa {
namespace deluxe {
/// Specialization of \c rosa::Agent for *sensor* role of the *deluxe
/// interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
/// \invariant There is a compatible *execution policy* set; the actual value in
/// \c rosa::deluxe::DeluxeSensor::MasterInputNextPos is valid with respect to
/// the corresponding types.
///
/// \see Definition of \c rosa::deluxe::DeluxeSensor::inv on the class invariant
///
/// \note All member functions validate the class invariant as part of their
/// precondition. Moreover, non-const functions validate the invariant before
/// return as their postcondition.
class DeluxeSensor : public Agent {
/// Checks whether \p this object holds the class invariant.
///
/// \see Invariant of the class \c rosa::deluxe::DeluxeSensor
///
/// \return if \p this object holds the class invariant
bool inv(void) const noexcept;
/// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of
/// \c this object.
std::unique_ptr<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::DeluxeSensor::master
const Token OutputType;
/// The type of values \p this object processes from its *master*.
///
/// That is the types of values \p this object receives from its *master* in a
/// \c rosa::deluxe::DeluxeTuple.
///
/// \see \c rosa::deluxe::DeluxeSensor::master
const Token MasterInputType;
private:
/// Indicates which element of the master-input is expected from the *master*.
///
/// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value
/// element by element in their order of definition. This member field tells
/// the element at which position should be received next.
///
/// \p this object is supposed to be triggered only when a complete
/// master-input has been received, that is the field should hold the value
/// `0`.
///
/// \see \c rosa::deluxe::DeluxeSensor::handleTrigger
/// \c rosa::deluxe::DeluxeSensor::saveMasterInput
token_size_t MasterInputNextPos;
/// Indicates whether the input value from the *master* has been changed since
/// the last trigger received from the system.
///
/// The flag is reset to \c false upon handling a trigger and then set to \c
/// true by \c rosa::deluxe::DeluxeSensor::saveMasterInput when storig a new
/// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue.
bool MasterInputChanged;
/// Stores the actual input value from *master*.
///
/// \note The type of the stored value matches the types indicated by \c
/// rosa::deluxe::DeluxeSensor::MasterInputType.
const std::unique_ptr<AbstractTokenizedStorage> MasterInputValue;
/// Alias for function objects used as trigger handler for
/// \c rosa::deluxe::DeluxeSensor.
///
/// \note The function used for \c H is to be \c noexcept.
///
/// \see \c DeluxeSensorTriggerHandlers
using H = std::function<void(void)>;
/// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of
/// rosa::deluxe::DeluxeSensor
///
/// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor
///
/// The actual data source functions and master-input processing function are
/// captured in lambda expressions that are in turn wrapped in \c
/// std::function objects. The lambda expression calls a processing function,
/// either to handle master-input or obtain the next sensory value from data
/// source. The next sensory value is sent it to *master* by calling \c
/// rosa::deluxe::DeluxeSensor::sendToMaster. Also, the flag \c
/// rosa::deluxe::DeluxeSensor::MasterInputChanged is reset when the current
/// value is passed to the master-input processing function. The function \c
/// rosa::deluxe::DeluxeSensor::handleTrigger needs only to call the proper
/// function object.
/// Processes master-input.
///
/// \ingroup DeluxeSensorTriggerHandlers
///
/// The function is called upon the sensor is trigged by the system.
const H MFP;
/// Produces the next sensory value during normal execution.
///
/// \ingroup DeluxeSensorTriggerHandlers
///
/// The function is used during normal execution. During simulation, the
/// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is
/// used instead of \c rosa::deluxe::DeluxeSensor::FP.
const H FP;
/// Produces the next sensory value during simulation.
///
/// \ingroup DeluxeSensorTriggerHandlers
///
/// The function is empty by default. The simulation environment sets it to be
/// used during simulation.
H SFP;
/// The *master* to send values to.
///
/// \note *Masters* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a
/// given moment.
Optional<AgentHandle> Master;
/// Tells the unique identifier of the *master* of \p this object, if any
/// registered.
///
/// \return the unique identifier of the *master*
///
/// \pre A *master* is registered for \p this object: \code
/// Master
/// \endcode
id_t masterId(void) const noexcept;
/// Wraps a master-input processing function into a trigger handler.
///
/// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers
///
/// \tparam Ts types of elements of master-input processed by \p MF
/// \tparam S0 indices for accessing master-input values
///
/// \param MF function that processes master-input
///
/// \note The second argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates
/// that \p this object does not receive master-input, \p MF is never called
/// if \p Ts is empty.
///
/// \return trigger handler function based on \p MF
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Ts... match \c
/// rosa::deluxe::DeluxeSensor::MasterInputType: \code
/// MasterInputType == DeluxeTuple<Ts...>::TT
/// \endcode
template <typename... Ts, size_t... S0>
H triggerHandlerFromProcessingFunction(
std::function<void(std::pair<DeluxeTuple<Ts...>, bool>)> &&MF,
Seq<S0...>) noexcept;
/// Wraps a data source function into a trigger handler.
///
/// \see \c rosa::deluxe::DeluxeSensor::FP, \c
/// rosa::deluxe::DeluxeSensor::SFP, and \c DeluxeSensorTriggerHandlers
///
/// \tparam T type of data provided by \p F
///
/// \param F function to generate value with
/// \param inSimulation if F is a data source for Simulation
///
/// \return trigger handler function based on \p F
///
/// \pre Statically, the type agument \p T is an instance of \c
/// rosa::deluxe::DeluxeTuple: \code
/// IsDeluxeTuple<T>::Value
/// \endcode Dynamically, \p T matches \c
/// rosa::deluxe::DeluxeSensor::OutputType: \code
/// OutputType == T::TT
/// \endcode
template <typename T>
H triggerHandlerFromDataSource(std::function<T(void)> &&F,
bool inSimulation) noexcept;
public:
/// Creates a new instance.
///
/// The constructor instantiates the base-class with functions to handle
/// messages as defined for the *deluxe interface*.
///
/// \todo Enforce \p F and \p MF do not potentially throw exception.
///
/// \tparam MT type of master-input handled by \p MF
/// \tparam T type of data to operate on
///
/// \note Instantiation fails if any of the type arguments \p MT and \p T is
/// not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c
/// rosa::deluxe::EmptyDeluxeTuple.
///
/// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed
/// object does not receive master-input.
///
/// \param Kind kind of the new \c rosa::Unit instance
/// \param Id unique identifier of the new \c rosa::Unit instance
/// \param Name name of the new \c rosa::Unit instance
/// \param S \c rosa::MessagingSystem owning the new instance
/// \param MF function to process master-input values with
/// \param F function to generate the next value with during normal operation
///
/// \pre Statically, \p MT and \p T are instances of \c
/// rosa::deluxe::DeluxeTuple and \p T contains at least one element:\code
/// TypeListAllDeluxeTuple<TypeList<MT, T>>::Value && T::Length > 0
/// \endcode
/// Dynamically, the instance is created as of kind
/// \c rosa::deluxe::atoms::SensorKind:
/// \code
/// Kind == rosa::deluxe::atoms::SensorKind
/// \endcode
///
/// \see \c rosa::deluxe::DeluxeTuple
template <
typename MT, typename T,
typename = std::enable_if_t<
TypeListAllDeluxeTuple<TypeList<MT, T>>::Value && (T::Length > 0)>>
DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept;
/// Destroys \p this object.
~DeluxeSensor(void) noexcept;
/// Returns the current execution policy of \p this object.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note The returned reference is valid only as long as \c
/// rosa::deluxe::DeluxeSensor::setExecutionPolicy() is not called and \p this
/// object is not destroyed.
///
/// \return \c rosa::deluxe::DeluxeSensor::ExecutionPolicy
const DeluxeExecutionPolicy &executionPolicy(void) const noexcept;
/// Sets the current execution policy of \p this object to \p EP.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note \p EP is set only if it can handle \p this object.
///
/// \param EP the new execution policy for \p this object
///
/// \return if \p EP was successfully set for \p this object.
bool setExecutionPolicy(std::unique_ptr<DeluxeExecutionPolicy> &&EP) noexcept;
/// The *master* of \p this object, if any.
///
/// \see \c rosa::deluxe::DeluxeSensor::registerMaster
///
/// \return the *master* registered for \p this object
Optional<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::DeluxeSensor::registerMaster should be
/// paired with a corresponding call of \c
/// rosa::deluxe::DeluxeAgent::registerSlave, which validates that
/// input/output types of master and slave matches.
///
/// \param _Master the *master* to register
///
/// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind:
/// \code
/// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
void registerMaster(const Optional<AgentHandle> _Master) noexcept;
/// Clears the simulation trigger handler of \p this object.
///
/// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr.
void clearSimulationDataSource(void) noexcept;
/// Tells whether a simulation trigger handler is set for \p this object.
///
/// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not
/// \c nullptr.
///
/// \return if a simulation trigger handler is set for \p this object.
bool simulationDataSourceIsSet(void) const noexcept;
/// Registers a simulation data source for \p this object.
///
/// A new simulation trigger handler wrapping \p SF is stored in
/// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered
/// simulation data source.
///
/// \todo Enforce SF does not potentially throw exception.
///
/// \tparam Ts types of elements of values provided by \p SF
///
/// \param SF function to generate value with
///
/// \pre \p Ts... match \c rosa::deluxe::DeluxeSensor::OutputType: \code
/// OutputType == TypeToken<Ts...>::Value
/// \endcode
template <typename... Ts>
void registerSimulationDataSource(
std::function<DeluxeTuple<Ts...>(void)> &&SF) noexcept;
private:
/// Sends a value to the *master* of \p this object.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it
/// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function
/// does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *master*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Value value to send
///
/// \note The second argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Ts match \c
/// rosa::deluxe::DeluxeSensor::OutputType: \code
/// OutputType == TypeToken<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToMaster(const DeluxeTuple<Ts...> &Value, Seq<S0...>) noexcept;
/// Handles master-input and generates the next sensory value upon trigger
/// from the system.
///
/// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input
/// and data generating function \c rosa::deluxe::DeluxeSensor::FP or \c
/// rosa::deluxe::DeluxeSensor::SFP if set.
///
/// \note The only argument is a \c rosa::AtomConstant, hence its actual
/// value is ignored.
///
/// \pre Master-input is supposed to be completely received upon triggering:
/// \code
/// MasterInputNextPos == 0
/// \endcode
void handleTrigger(atoms::Trigger) noexcept;
/// Stores a new input value from the *master*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the
/// flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. The function also
/// takes care of checking and updating \c
/// rosa::deluxe::DeluxeSensor::MasterInputNextPos: increments its value and
/// resets it to `0` when the last element is received.
///
/// \note Utilized by member functions of group \c
/// DeluxeSensorMasterInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of the *master*
/// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
/// \param Value the input value to store
///
/// \pre The *master* with \p Id is registered, \p Pos is the expected
/// position of master-input, and the input from the *master* at position \p
/// Pos is expected to be of type \p T: \code
/// Master && masterId() == Id && Pos == MasterInputNextPos &&
/// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value
/// \endcode
template <typename T>
void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept;
/// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of
/// rosa::deluxe::DeluxeSensor
///
/// Definition of member functions handling messages from the *master* with
/// different types of input
///
/// A *slave* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *master*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeSensor::saveMasterInput with
/// the proper template argument and pass the content of the message on for
/// processing.
///
/// \note The member functions in this group are defined by \c
/// DSMASTERHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DSMASTERHANDLERDEF(AtomValue)
DSMASTERHANDLERDEF(int16_t)
DSMASTERHANDLERDEF(int32_t)
DSMASTERHANDLERDEF(int64_t)
DSMASTERHANDLERDEF(int8_t)
DSMASTERHANDLERDEFN(long double, long_double)
DSMASTERHANDLERDEFN(std::string, std__string)
DSMASTERHANDLERDEF(uint16_t)
DSMASTERHANDLERDEF(uint32_t)
DSMASTERHANDLERDEF(uint64_t)
DSMASTERHANDLERDEF(uint8_t)
DSMASTERHANDLERDEF(unit_t)
DSMASTERHANDLERDEF(bool)
DSMASTERHANDLERDEF(double)
DSMASTERHANDLERDEF(float)
/// @}
};
template <typename... Ts, size_t... S0>
DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction(
std::function<void(std::pair<DeluxeTuple<Ts...>, bool>)> &&MF,
Seq<S0...>) noexcept {
using MT = DeluxeTuple<Ts...>;
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(MasterInputType == MT::TT);
- // NOTE: Clang 6 warns about unused lambda captures; we suppress that
- // warning (those variables need to be captured).
+// NOTE: Clang 6 warns about unused lambda captures; we suppress that
+// warning (those variables need to be captured).
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-lambda-capture"
#endif // defined __clang__
return [ this, MF ](void) noexcept {
// Do not do anything for master-input type \c
// rosa::deluxe::EmptyDeluxeTuple.
- if constexpr (!std::is_same<MT, EmptyDeluxeTuple>::value) {
- LOG_TRACE_STREAM << "DeluxeSensor " << FullName
- << " handles master-input." << std::endl;
- // The assert must hold if \p this object was successfuuly constructed.
- ASSERT((true && ... &&
- (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
- const auto MasterInputArg = std::make_pair(
- // Get all elements of the tuple in a fold expression.
- DeluxeTuple<Ts...>(*static_cast<const Ts *>(
- MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
- MasterInputChanged);
- MasterInputChanged = false;
- MF(MasterInputArg);
- }
+ if
+ constexpr(!std::is_same<MT, EmptyDeluxeTuple>::value) {
+ LOG_TRACE_STREAM << "DeluxeSensor " << FullName
+ << " handles master-input." << std::endl;
+ // The assert must hold if \p this object was successfully constructed.
+ STATIC_ASSERT(
+ (true && ... &&
+ (static_cast<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.
+ DeluxeTuple<Ts...>(*static_cast<const Ts *>(
+ MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
+ MasterInputChanged);
+ MasterInputChanged = false;
+ MF(MasterInputArg);
+ }
};
#ifdef __clang__
#pragma clang diagnostic pop
#endif // defined __clang__
}
template <typename T>
DeluxeSensor::H
DeluxeSensor::triggerHandlerFromDataSource(std::function<T(void)> &&F,
bool inSimulation) noexcept {
STATIC_ASSERT(IsDeluxeTuple<T>::Value, "not tuple type argument");
ASSERT(OutputType == T::TT);
return [ this, F, inSimulation ](void) noexcept {
// Get value and send it to master only if \p ExecutionPolicy allows it.
if (ExecutionPolicy->shouldProcess({})) {
LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value."
<< std::endl;
sendToMaster(F(), seq_t<T::Length>());
} else {
LOG_TRACE_STREAM << "DeluxeSensor " << Name << " skips next value."
<< std::endl;
if (inSimulation) {
// But read input value in Simulation anyway as input values are
// provided for the highest execution frequency for simulation
F();
}
}
};
}
template <typename MT, typename T, typename>
DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id,
const std::string &Name, MessagingSystem &S,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept
: Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger),
DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t),
DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t),
DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double),
DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t),
DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t),
DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t),
DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double),
DSMASTERHANDLERREF(float)),
ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT),
MasterInputType(MT::TT), MasterInputChanged(false),
MasterInputValue(new typename TokenizedStorageForTypeList<
typename UnwrapDeluxeTuple<MT>::Type>::Type()),
MFP(triggerHandlerFromProcessingFunction(std::move(MF),
seq_t<MT::Length>())),
FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) {
ASSERT(Kind == atoms::SensorKind);
LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " is created."
<< std::endl;
ASSERT(inv());
}
template <typename... Ts>
void DeluxeSensor::registerSimulationDataSource(
std::function<DeluxeTuple<Ts...>(void)> &&SF) noexcept {
ASSERT(OutputType == TypeToken<Ts...>::Value);
SFP = triggerHandlerFromDataSource(std::move(SF), true);
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeSensor::sendToMaster(const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(OutputType == TypeToken<Ts...>::Value);
- // The assert must hold if \p this object was successfuuly constructed.
- ASSERT((true && ... &&
- (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ // 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 << "DeluxeSensor " << FullName << "(" << Id
<< ") sends to master("
<< static_cast<bool>(Master && *Master) << "): " << Value
<< std::endl;
// There is a handle and the referred *master* is in a valid state.
if (Master && *Master) {
// Handle each element of the tuple in a fold expression.
(Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0],
std::get<S0>(Value))),
...);
}
ASSERT(inv());
}
template <typename T>
void DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos,
T Value) noexcept {
ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos &&
typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value);
LOG_TRACE_STREAM << "DeluxeSensor " << 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;
}
} // End namespace deluxe
} // End namespace rosa
#undef DSMASTERHANDLEREF
#undef DSMASTERHANDLEDEF
#undef DSMASTERHANDLEDEFN
#undef DSMASTERHANDLENAME
#endif // ROSA_DELUXE_DELUXESENSOR_HPP
diff --git a/include/rosa/support/atom.hpp b/include/rosa/support/atom.hpp
index 1dfe3e0..55d51c4 100644
--- a/include/rosa/support/atom.hpp
+++ b/include/rosa/support/atom.hpp
@@ -1,209 +1,211 @@
//===-- rosa/support/atom.hpp -----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/atom.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Facility for *atoms*, short strings statically encoded as integers.
///
/// \note This implementation is based on the \c atom implementation of CAF.
/// \todo Check license.
///
/// *Atoms* can be used to turn short string literals into statically generated
/// types. The literals may consist of at most \c 10 non-special characters,
/// legal characters are \c _0-9A-Za-z and the whitespace character. Special
/// characters are turned into whitespace, which may result in different string
/// literals being encoded into the same integer value, if any of those contain
/// at least one special character.
///
/// \note The usage of special characters in the string literals used to create
/// *atoms* cannot be checked by the compiler.
///
/// Example:
///
/// \code
/// constexpr AtomValue NameValue = atom("name");
/// using NameAtom = AtomConstant<NameValue>;
///
/// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value)
/// \endcode
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_ATOM_HPP
#define ROSA_SUPPORT_ATOM_HPP
#include "rosa/support/debug.hpp"
+#include <string>
+#include <cstring>
namespace rosa {
/// Maximal length of valid atom strings.
constexpr size_t MaxAtomLength = 10;
/// Underlying integer type of atom values.
using atom_t = uint64_t;
/// Turn \c rosa::atom_t into a strongly typed enumeration.
///
/// Values of \c rosa::atom_t casted to \c rosa::AtomValue may be used in a
/// type-safe way.
enum class AtomValue : atom_t {};
/// Anonymous namespace with implementational details, consider it private.
namespace {
// clang-format off
/// Encodes ASCII characters to 6-bit encoding.
constexpr unsigned char AtomEncodingTable[] = {
/* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */
/* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0,
/* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
/* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37,
/* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
/* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0};
// clang-format on
/// Decodes 6-bit characters to ASCII
constexpr char AtomDecodingTable[] = " 0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
"abcdefghijklmnopqrstuvwxyz";
/// Encodes one character and updates the integer representation.
///
/// \param Current an encoded value
/// \param CharCode a character to add to \p Current
///
/// \return \p Current updated with \p CharCode
constexpr atom_t nextInterim(atom_t Current, size_t CharCode) {
return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0];
}
/// Encodes a C-string into an integer value to be used as \c rosa::AtomValue.
///
/// \param CStr a string to encode
/// \param Interim encoded value to add \p CStr to it
///
/// \return \p Interim updated with \p CStr
constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) {
return (*CStr == '\0')
? Interim
: atomValue(CStr + 1,
nextInterim(Interim, static_cast<size_t>(*CStr)));
}
} // End namespace
/// Converts a \c std::string into a \c rosa::AtomValue.
///
/// \param S \c std::string to convert
///
/// \return \c rosa::AtomValue representing \p S
AtomValue atom_from_string(const std::string &S);
/// Converts a string-literal into a \c rosa::AtomValue.
///
/// \tparam Size the length of \p Str
///
/// \param Str the string-literal to convert
///
/// \return \c rosa::AtomValue representating \p Str
///
/// \pre \p Str is not too long:\code
/// Size <= MaxAtomLength + 1
/// \endcode
template <size_t Size> constexpr AtomValue atom(char const (&Str)[Size]) {
// Last character is the NULL terminator.
STATIC_ASSERT(Size <= MaxAtomLength + 1,
"Too many characters in atom definition");
return static_cast<AtomValue>(atomValue(Str));
}
/// Lifts a \c rosa::AtomValue to a compile-time constant.
///
/// \tparam V \c rosa::AtomValue to lift
template <AtomValue V> struct AtomConstant {
/// Constructor has to do nothing.
constexpr AtomConstant(void) {}
/// Returns the wrapped value.
///
/// \return \p V
constexpr operator AtomValue(void) const { return V; }
/// Returns the wrapped value as of type \c rosa::atom_t.
///
/// \return \c rosa::atom_t value from \p V
static constexpr atom_t value() { return static_cast<atom_t>(V); }
/// An instance *of this constant* (*not* a \c rosa::AtomValue).
static const AtomConstant Value;
};
// Implementation of the static member field \c rosa::AtomConstant::Value.
template <AtomValue V>
const AtomConstant<V> AtomConstant<V>::Value = AtomConstant<V>{};
} // End namespace rosa
namespace std {
/// Converts a \c rosa::AtomValue into \c std::string.
///
/// \param What value to convert
///
/// \return \c std::string encoded in \p What
string to_string(const rosa::AtomValue &What);
/// Dumps a \c rosa::AtomValue to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
/// \param A \c rosa::AtomValue to dump
///
/// \return \p OS after dumping \p N to it
inline ostream &operator<<(ostream &OS, const rosa::AtomValue &A) {
OS << to_string(A);
return OS;
}
/// Converts a \c rosa::AtomConstant into \c std::string.
///
/// \tparam V \c rosa::AtomValue to convert
///
/// \note The actual argument of type `const rosa::AtomConstant<V>` is ignored
/// because the \c rosa::AtomValue to convert is encoded in the type itself.
///
/// \return the original string encoded in \p V
template <rosa::AtomValue V> string to_string(const rosa::AtomConstant<V> &) {
return to_string(V);
}
/// Dumps a \c rosa::AtomConstant to a given \c std::ostream.
///
/// \tparam V the \c rosa::AtomValue to dump
///
/// \param [in,out] OS output stream to dump to
/// \param A \c rosa::AtomConstant providing \p V
///
/// \return \p OS after dumping \p V to it
template <rosa::AtomValue V>
inline ostream &operator<<(ostream &OS, const rosa::AtomConstant<V> &A) {
(void)A; // Shut compiler about unused parameter.
OS << to_string(V);
return OS;
}
} // End namespace std
#endif // ROSA_SUPPORT_ATOM_HPP
diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp
index dca7f4e..2bd2c77 100755
--- a/include/rosa/support/tokenized_storages.hpp
+++ b/include/rosa/support/tokenized_storages.hpp
@@ -1,620 +1,623 @@
//===-- rosa/support/tokenized_storages.hpp ---------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/tokenized_storages.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Definition of storage helper template for storing values in a
/// type-safe way based on type tokens.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
#define ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
+#include "rosa/support/log.h"
#include "rosa/support/type_token.hpp"
#include <memory>
#include <vector>
namespace rosa {
/// Defines a simple interface for storing and accessing values of different
/// types.
///
/// While the interface provides features to access values and know their
/// types, it is the users responsibility to use particular values according to
/// their actual types. No facilities for type-safe access of values is
/// provided by the class.
///
/// \see \c rosa::TokenizedStorage for a type-safe specialization of the
/// interface.
class AbstractTokenizedStorage {
protected:
/// Protected constructor restricts instantiation for derived classes.
AbstractTokenizedStorage(void) noexcept = default;
public:
/// No copying and moving of \c rosa::AbstractTokenizedStorage instances.
///@{
- AbstractTokenizedStorage(const AbstractTokenizedStorage&) = delete;
- AbstractTokenizedStorage &operator=(const AbstractTokenizedStorage&) = delete;
- AbstractTokenizedStorage(AbstractTokenizedStorage&& Other) = delete;
- AbstractTokenizedStorage &operator=(AbstractTokenizedStorage&&) = delete;
+ AbstractTokenizedStorage(const AbstractTokenizedStorage &) = delete;
+ AbstractTokenizedStorage &
+ operator=(const AbstractTokenizedStorage &) = delete;
+ AbstractTokenizedStorage(AbstractTokenizedStorage &&Other) = delete;
+ AbstractTokenizedStorage &operator=(AbstractTokenizedStorage &&) = delete;
///@}
/// Destroys \p this object.
virtual ~AbstractTokenizedStorage(void) noexcept = default;
/// Tells how many values are stored in \p this object.
///
/// \return number of values stored in \p this object
virtual size_t size(void) const noexcept = 0;
/// Tells the type of the value stored at a position.
///
/// \param Pos the index of the value whose type is to returned
///
/// \return \c rosa::TypeNumber for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
virtual TypeNumber typeAt(const token_size_t Pos) const noexcept = 0;
/// Provides an untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
virtual void *pointerTo(const token_size_t Pos) noexcept = 0;
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return constant untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0;
};
/// Template class storing values and providing dynamic type-safe access to
/// them in a lightweight way based on type tokens.
///
/// \see rosa/support/type_token.hpp
///
/// \tparam Types types whose values are to be stored
template <typename... Types> class TokenizedStorage;
/// \defgroup TokenizedStorageForTypeList Implementation of
/// rosa::TokenizedStorageForTypeList
///
/// \brief Transforms a \c rosa::TypeList instance to the corresponding
/// \c rosa::TokenizedStorage instance.
///
/// A \c rosa::TypeList \c List instance can be turned into a corresponding \c
/// rosa::TokenizedStorage instance as \code
/// typename TokenizedStorageForTypeList<List>::Type
/// \endcode
///
/// For example, the following expression evaluates to `true`: \code
/// std::is_same<typename TokenizedStorageForTypeList<TypeList<T1, T2>>::Type,
/// TokenizedStorage<T1, T2>>::value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to transform
template <typename List> struct TokenizedStorageForTypeList;
/// Implementation of the template for \c rosa::TypeList instances.
-template <typename... Ts>
-struct TokenizedStorageForTypeList<TypeList<Ts...>> {
+template <typename... Ts> struct TokenizedStorageForTypeList<TypeList<Ts...>> {
using Type = TokenizedStorage<Ts...>;
};
///@}
/// Nested namespace with implementation for \c rosa::TokenizedStorage, consider
/// it private.
namespace {
/// Initializes a pre-allocated memory area with values from constant lvalue
/// references.
///
/// \tparam Types types whose values are to be stored
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts the values to store in \p Arena
///
/// \note \p Arena needs to be a valid pointer to a memory area big enough for
/// values of \p Types.
template <typename... Types>
inline void createArenaElements(void *const Arena,
const Types &... Ts) noexcept;
-/// \defgroup createLvalueArenaElement Implementation of creating lvalue arena elements
+/// \defgroup createLvalueArenaElement Implementation of creating lvalue arena
+/// elements
///
/// Stores values from constant lvalue references into a pre-allocated memory
/// area.
///
/// \note To be used by the implementation of \c createArenaElements.
///
/// \todo Document these functions.
///@{
/// \note This terminal case is used for both constant lvalue references and
/// value references.
template <size_t Pos>
inline void createArenaElement(void *const,
const std::vector<size_t> &Offsets) {
ASSERT(Pos == Offsets.size());
}
template <size_t Pos, typename Type, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets,
const Type &T, const Types &... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
new (static_cast<Type *>(static_cast<void *>(static_cast<uint8_t *>(Arena) +
Offsets[Pos]))) Type(T);
createArenaElement<Pos + 1>(Arena, Offsets, Ts...);
}
template <size_t Pos, AtomValue V, typename... Types>
inline void
createArenaElement(void *const Arena, const std::vector<size_t> &Offsets,
const AtomConstant<V> &, const Types &... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
*static_cast<AtomValue *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos])) = V;
createArenaElement<Pos + 1>(Arena, Offsets, Ts...);
}
///@}
/// Implementation of the template.
///
/// \tparam Types types of values to store
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts values to store in \p Arena
///
/// \pre \p Arena is not \p nullptr.
template <typename... Types>
inline void createArenaElements(void *const Arena,
const Types &... Ts) noexcept {
ASSERT(Arena != nullptr);
createArenaElement<0>(Arena, TokenizedStorage<Types...>::Offsets, Ts...);
}
/// Initializes a pre-allocated memory area with values from rvalue references.
///
/// \tparam Types types whose values are to be stored
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts the values to store in \p Arena
///
/// \note \p Arena needs to be a valid pointer to a memory area big enough for
/// values of \p Types.
template <typename... Types>
inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept;
-/// \defgroup createRvalueArenaElement Implementation of creating rvalue arena elements
+/// \defgroup createRvalueArenaElement Implementation of creating rvalue arena
+/// elements
///
/// Stores values from rvalue references into a pre-allocated memory area.
///
/// \note To be used by the implementation of \c createArenaElements.
///
/// \todo Document these functions.
///@{
template <size_t Pos, typename Type, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets, Type &&T,
Types &&... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
new (static_cast<Type *>(static_cast<void *>(
static_cast<uint8_t *>(Arena) + Offsets[Pos]))) Type(std::move(T));
createArenaElement<Pos + 1>(Arena, Offsets, std::move(Ts)...);
}
template <size_t Pos, AtomValue V, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets,
AtomConstant<V> &&, Types &&... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
*static_cast<AtomValue *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos])) = V;
createArenaElement<Pos + 1>(Arena, Offsets, std::move(Ts)...);
}
///@}
/// Implementation of the template.
///
/// \tparam Types types of values to store
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts values to store in \p Arena
///
/// \pre \p Arena is not \c nullptr.
template <typename... Types>
inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept {
ASSERT(Arena != nullptr);
createArenaElement<0>(Arena, TokenizedStorage<Types...>::Offsets,
std::move(Ts)...);
}
/// Destroys values allocated by \c createArenaElements.
///
/// \tparam Types types whose values are stored in \p Arena
///
/// \param Arena the memory area to destroy values from
///
/// \note \p Arena needs to be a valid pointer to a memory area where values of
/// \p Types are stored.
template <typename... Types>
inline void destroyArenaElements(void *const Arena) noexcept;
/// \defgroup destroyArenaElement Implementation of destroying arena elements
///
/// Destroys values from a memory area.
///
/// \note To be used by the implementation of \c destroyArenaElements.
///
/// \todo Document these functions.
///@{
template <size_t Pos>
inline void destroyArenaElement(void *const,
const std::vector<size_t> &Offsets) noexcept {
ASSERT(Pos == Offsets.size());
}
template <size_t Pos, typename Type, typename... Types>
inline void destroyArenaElement(void *const Arena,
const std::vector<size_t> &Offsets) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
static_cast<Type *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos]))
->~Type();
destroyArenaElement<Pos + 1, Types...>(Arena, Offsets);
}
///@}
/// Implementation of the template.
///
/// \tparam Types types of values to destroy
///
/// \param Arena the memory area to destroy values from
///
/// \pre \p Arena is not \c nullptr.
template <typename... Types>
inline void destroyArenaElements(void *const Arena) noexcept {
ASSERT(Arena != nullptr);
destroyArenaElement<0, Types...>(Arena, TokenizedStorage<Types...>::Offsets);
}
} // End namespace
/// Implementation of the template \c rosa::TokenizedStorage as a
/// specialization of \c rosa::AbstractTokenizedStorage.
///
/// The class provides facilities for storing values and providing type-safe
/// access to them.
///
/// \tparam Types types of values to store
template <typename... Types>
class TokenizedStorage : public AbstractTokenizedStorage {
public:
/// \c rosa::Token for the stored values.
static constexpr Token ST = TypeToken<std::decay_t<Types>...>::Value;
/// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena.
static const std::vector<size_t> Offsets;
private:
/// A BLOB storing all the values one after the other.
void *const Arena;
/// Generates byte offsets for accessing values stored in
/// \c rosa::TokenizedStorage::Arena.
///
/// \return \c std::vector containing byte offsets for accessing values stored
/// in \c rosa::TokenizedStorage::Arena
static std::vector<size_t> offsets(void) noexcept {
- Token T = ST; // Need a mutable copy.
+ Token T = ST; // Need a mutable copy.
const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T.
- std::vector<size_t> O(N); // Allocate vector of proper size.
+ std::vector<size_t> O(N); // Allocate vector of proper size.
// Do nothing for 0 elements.
if (N > 0) {
token_size_t I = 0; // Start indexing from position \c 0.
- O[0] = 0; // First offset is always \c 0.
+ O[0] = 0; // First offset is always \c 0.
while (I < N - 1) {
ASSERT(I + 1 < O.size() && lengthOfToken(T) == N - I);
// Calculate next offset based on the previous one.
// \note The offset of the last value is stored at `O[N - 1]`, which is
// set when `I == N - 2`. Hence the limit of the loop.
O[I + 1] = O[I] + sizeOfHeadOfToken(T);
- dropHeadOfToken(T), ++I;
+ dropHeadOfToken(T);
+ ++I;
}
ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1);
}
return O;
}
public:
/// Creates an instance with default values.
///
/// \note This constructor requires that all actual template arguments \p
/// Types... are default constructible.
TokenizedStorage(void) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
createArenaElements(Arena, Types()...);
}
/// Creates an instance from constant lvalue references.
///
/// \param Ts values to store
TokenizedStorage(const std::decay_t<Types> &... Ts) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
createArenaElements(Arena, Ts...);
}
/// Creates an instance from rvalue references.
///
/// \param Ts values to store
TokenizedStorage(std::decay_t<Types> &&... Ts) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
createArenaElements(Arena, std::move(Ts)...);
}
/// No copying and moving of \c rosa::TokenizedStorage instances.
///
/// \note This restriction may be relaxed as moving should be easy to
/// implement, only requires the possiblity to validate Arena pointer.
///@{
- TokenizedStorage(const TokenizedStorage&) = delete;
- TokenizedStorage &operator=(const TokenizedStorage&) = delete;
- TokenizedStorage(TokenizedStorage&& Other) = delete;
- TokenizedStorage &operator=(TokenizedStorage&&) = delete;
+ TokenizedStorage(const TokenizedStorage &) = delete;
+ TokenizedStorage &operator=(const TokenizedStorage &) = delete;
+ TokenizedStorage(TokenizedStorage &&Other) = delete;
+ TokenizedStorage &operator=(TokenizedStorage &&) = delete;
///@}
// Destroys \p this object.
~TokenizedStorage(void) {
destroyArenaElements<std::decay_t<Types>...>(Arena);
::operator delete(Arena);
}
/// Tells how many values are stored in \p this object.
///
/// \return number of values stored in \p this object
- size_t size(void) const noexcept override {
- return Offsets.size();
- }
+ size_t size(void) const noexcept override { return Offsets.size(); }
/// Tells the type of the value stored at a position.
///
/// \param Pos the index of the value whose type is to returned
///
/// \return \c rosa::TypeNumber for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
TypeNumber typeAt(const token_size_t Pos) const noexcept override {
ASSERT(Pos < size());
Token TT = ST;
dropNOfToken(TT, Pos);
return headOfToken(TT);
}
/// Provides an untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
void *pointerTo(const token_size_t Pos) noexcept override {
ASSERT(Pos < size());
return static_cast<uint8_t *>(Arena) + Offsets[Pos];
}
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return constant untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
const void *pointerTo(const token_size_t Pos) const noexcept override {
ASSERT(Pos < size());
return static_cast<const uint8_t *>(Arena) + Offsets[Pos];
}
/// Tells if the value stored at a given index is of a given type.
///
/// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as
/// the \c rosa::AtomValue wrapped into it.
///
/// \tparam T type to match against
///
/// \param Pos index the type of the value at is to be matched against \p Type
///
/// \return if the value at index \p Pos of type \p T
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
template <typename T> bool isTypeAt(const size_t Pos) const noexcept {
ASSERT(Pos < size());
Token TT = ST;
dropNOfToken(TT, Pos);
return isHeadOfTokenTheSameType<T>(TT);
}
/// Gives a reference of a value of a given type stored at a given index.
///
/// \note The constant variant of the function relies on this implementation,
/// the function may not modify \p this object!
///
/// \tparam T type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return reference of \p T for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p T:
/// \code
/// Pos < Size && isTypeAt<T>(Pos)
/// \endcode
template <typename T> T &valueAt(const token_size_t Pos) noexcept {
ASSERT(Pos < size() && isTypeAt<T>(Pos));
return *static_cast<T *>(pointerTo(Pos));
}
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam T type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return constant reference of \p T for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p T:
/// \code
/// Pos < Size && isTypeAt<T>(Pos)
/// \endcode
template <typename T>
const T &valueAt(const token_size_t Pos) const noexcept {
// \note Just use the non-const implementation as that does not modify
// \p this object.
return const_cast<TokenizedStorage *>(this)->valueAt<T>(Pos);
}
};
// Implementation of the static member field \c rosa::TokenizedStorage::Offsets.
template <typename... Types>
const std::vector<size_t>
TokenizedStorage<Types...>::Offsets = TokenizedStorage<Types...>::offsets();
/// Specialization of the template \c rosa::TokenizedStorage for storing
/// nothing.
///
/// \note The specialization implements the interface defined by \c
/// rosa::AbstractTokenizedStorage but most of the functions cannot be called
/// because nothing is stored in instances of the class.
template <> class TokenizedStorage<> : public AbstractTokenizedStorage {
public:
/// \c rosa::Token for the stored values.
static constexpr Token ST = TypeToken<>::Value;
/// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena.
static const std::vector<size_t> Offsets;
/// Creates an instance.
TokenizedStorage(void) noexcept {}
/// No copying and moving of \c rosa::TokenizedStorage instances.
///
/// \note This restriction may be relaxed as moving should be easy to
/// implement, only requires the possiblity to validate Arena pointer.
///@{
TokenizedStorage(const TokenizedStorage &) = delete;
TokenizedStorage &operator=(const TokenizedStorage &) = delete;
TokenizedStorage(TokenizedStorage &&Other) = delete;
TokenizedStorage &operator=(TokenizedStorage &&) = delete;
///@}
// Destroys \p this object.
- ~TokenizedStorage(void) {}
+ ~TokenizedStorage(void) override {}
/// Tells how many values are stored in \p this object.
///
/// \return `0`
size_t size(void) const noexcept override { return 0; }
/// Tells the type of the value stored at a position.
///
/// \pre Do not call.
TypeNumber typeAt(const token_size_t) const noexcept override {
- ASSERT(false);
+ LOG_ERROR("Called unsupported function");
return TypeNumber(0);
}
/// Provides an untyped pointer for the value stored at a position.
///
/// \pre Do not call.
void *pointerTo(const token_size_t) noexcept override {
- ASSERT(false);
+ LOG_ERROR("Called unsupported function");
return nullptr;
}
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \pre Do not call.
const void *pointerTo(const token_size_t) const noexcept override {
- ASSERT(false);
+ LOG_ERROR("Called unsupported function");
return nullptr;
}
/// Tells if the value stored at a given index is of a given type.
///
/// \pre Do not call.
template <typename> bool isTypeAt(const size_t) const noexcept {
- ASSERT(false);
+ LOG_ERROR("Called unsupported function");
return false;
}
/// Gives a reference of a value of a given type stored at a given index.
///
/// \tparam T type to give a reference of
/// \pre Do not call.
template <typename T> T &valueAt(const token_size_t) noexcept {
- ASSERT(false);
+ LOG_ERROR("Called unsupported function");
return *static_cast<T *>(nullptr);
}
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam T type to give a reference of
///
/// \pre Do not call.
template <typename T> const T &valueAt(const token_size_t) const noexcept {
+ LOG_ERROR("Called unsupported function");
// \note Just use the non-const implementation as that does not modify
// \p this object.
return *static_cast<T *>(nullptr);
}
};
} // End namespace rosa
#endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
diff --git a/lib/support/atom.cpp b/lib/support/atom.cpp
index 9ef7c43..8ce75ec 100644
--- a/lib/support/atom.cpp
+++ b/lib/support/atom.cpp
@@ -1,59 +1,57 @@
//===-- support/atom.cpp ----------------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file support/atom.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of non-static part of atom facilities of
/// rosa/support/atom.hpp.
///
/// \note This implementation is based on the `atom` implementation of CAF.
/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/atom.hpp"
-#include <cstring>
-
namespace rosa {
AtomValue atom_from_string(const std::string &S) {
if (S.size() > MaxAtomLength) {
return atom("");
}
char AtomBuf[MaxAtomLength + 1];
std::memcpy(AtomBuf, S.c_str(), S.size());
AtomBuf[S.size()] = '\0';
return atom(AtomBuf);
}
} // End namespace rosa
namespace std {
string to_string(const rosa::AtomValue &What) {
auto X = static_cast<rosa::atom_t>(What);
string S;
S.reserve(rosa::MaxAtomLength + 1);
// Don't read characters before we found the leading 0xF.
// First four bits set?
bool ReadChars = ((X & 0xF000000000000000) >> 60) == 0xF;
uint64_t Mask = 0x0FC0000000000000;
for (int BitShift = 54; BitShift >= 0; BitShift -= 6, Mask >>= 6) {
if (ReadChars) {
S += rosa::AtomDecodingTable[(X & Mask) >> BitShift];
} else if (((X & Mask) >> BitShift) == 0xF) {
ReadChars = true;
}
}
return S;
}
} // End namespace std
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Mar 1, 9:55 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
289208
Default Alt Text
(212 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment