diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp index f177e5b..4b99535 100644 --- a/apps/ccam/ccam.cpp +++ b/apps/ccam/ccam.cpp @@ -1,309 +1,326 @@ //===-- 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 #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 #include #include #include #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) { // START MAXI TESTING std::shared_ptr> TestPartFunc( new PartialFunction( { {{0.f, 3.f}, std::make_shared>(0.f, 1.f / 3)}, {{3.f, 6.f}, std::make_shared>(1.f, 0.f)}, {{6.f, 9.f}, std::make_shared>(3.f, -1.f / 3)}, }, 0)); std::shared_ptr> TestStepFunc( new StepFunction(1 / 10)); SystemStateDetector TestSystemStateDetector(1000, 5, TestPartFunc, TestPartFunc); /* SignalStateInformation TestSigStateInfo( 0, SignalProperties::INPUT, 5, 5, 5, TestPartFunc, TestPartFunc, TestStepFunc, TestStepFunc, TestPartFunc, TestPartFunc); */ std::vector> testVector; TestSystemStateDetector.detectSystemState(testVector); // END MAXI TESTING 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 C = DeluxeContext::create(AppName); std::shared_ptr> BrokenDelayFunction( new PartialFunction( {{{0, AppConfig.BrokenCounter}, std::make_shared>( 0, 0.f, AppConfig.BrokenCounter, 1.f)}, {{AppConfig.BrokenCounter, std::numeric_limits::max()}, std::make_shared>(1.f, 0.f)}}, 0.f)); std::shared_ptr> OkDelayFunction( new PartialFunction( {{{0, AppConfig.BrokenCounter}, std::make_shared>( 0, 1.f, AppConfig.BrokenCounter, 0.f)}, {{AppConfig.BrokenCounter, std::numeric_limits::max()}, std::make_shared>(0.f, 0.f)}}, 1.f)); // // Create a DeluxeAgent with SystemStateDetector functionality. // LOG_INFO("Create SystemStateDetector agent."); AgentHandle SystemStateDetectorAgent = createSystemStateDetectorAgent( C, "SystemStateDetector", AppConfig.SignalConfigurations.size(), BrokenDelayFunction, OkDelayFunction); + C->setExecutionPolicy(SystemStateDetectorAgent, + DeluxeExecutionPolicy::awaitAll({})); LOG_INFO("Creating sensors, SignalStateDetector functionalities and their " "Abstractions."); std::vector Sensors; std::vector>> SampleMatchesFunctions; std::vector>> SampleMismatchesFunctions; std::vector>> SignalIsStableFunctions; std::vector>> SignalIsDriftingFunctions; std::vector>> NumOfSamplesMatchFunctions; std::vector>> NumOfSamplesMismatchFunctions; std::vector>> SignalStateDetectors; std::vector SignalStateDetectorAgents; for (auto SignalConfiguration : AppConfig.SignalConfigurations) { // // Create deluxe sensors. // Sensors.emplace_back(C->createSensor(SignalConfiguration.Name)); + C->setExecutionPolicy(Sensors.back(), DeluxeExecutionPolicy::decimation(1)); // // Create functionalities for SignalStateDetector. // SampleMatchesFunctions.emplace_back(new PartialFunction( { {{-SignalConfiguration.OuterBound, -SignalConfiguration.InnerBound}, std::make_shared>( -SignalConfiguration.OuterBound, 0.f, -SignalConfiguration.InnerBound, 1.f)}, {{-SignalConfiguration.InnerBound, SignalConfiguration.InnerBound}, std::make_shared>(1.f, 0.f)}, {{SignalConfiguration.InnerBound, SignalConfiguration.OuterBound}, std::make_shared>( SignalConfiguration.InnerBound, 1.f, SignalConfiguration.OuterBound, 0.f)}, }, 0)); SampleMismatchesFunctions.emplace_back(new PartialFunction( { {{-SignalConfiguration.OuterBound, -SignalConfiguration.InnerBound}, std::make_shared>( -SignalConfiguration.OuterBound, 1.f, -SignalConfiguration.InnerBound, 0.f)}, {{-SignalConfiguration.InnerBound, SignalConfiguration.InnerBound}, std::make_shared>(0.f, 0.f)}, {{SignalConfiguration.InnerBound, SignalConfiguration.OuterBound}, std::make_shared>( SignalConfiguration.InnerBound, 0.f, SignalConfiguration.OuterBound, 1.f)}, }, 1)); SignalIsStableFunctions.emplace_back(new PartialFunction( { {{-SignalConfiguration.OuterBoundDrift, -SignalConfiguration.InnerBoundDrift}, std::make_shared>( -SignalConfiguration.OuterBoundDrift, 0.f, -SignalConfiguration.InnerBoundDrift, 1.f)}, {{-SignalConfiguration.InnerBoundDrift, SignalConfiguration.InnerBoundDrift}, std::make_shared>(1.f, 0.f)}, {{SignalConfiguration.InnerBoundDrift, SignalConfiguration.OuterBoundDrift}, std::make_shared>( SignalConfiguration.InnerBoundDrift, 1.f, SignalConfiguration.OuterBoundDrift, 0.f)}, }, 0)); SignalIsDriftingFunctions.emplace_back(new PartialFunction( { {{-SignalConfiguration.OuterBoundDrift, -SignalConfiguration.InnerBoundDrift}, std::make_shared>( -SignalConfiguration.OuterBoundDrift, 1.f, -SignalConfiguration.InnerBoundDrift, 0.f)}, {{-SignalConfiguration.InnerBoundDrift, SignalConfiguration.InnerBoundDrift}, std::make_shared>(0.f, 0.f)}, {{SignalConfiguration.InnerBoundDrift, SignalConfiguration.OuterBoundDrift}, std::make_shared>( SignalConfiguration.InnerBoundDrift, 0.f, SignalConfiguration.OuterBoundDrift, 1.f)}, }, 1)); NumOfSamplesMatchFunctions.emplace_back(new StepFunction( 1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepUp)); NumOfSamplesMismatchFunctions.emplace_back(new StepFunction( 1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepDown)); // // Create SignalStateDetector functionality // SignalStateDetectors.emplace_back( new SignalStateDetector( SignalConfiguration.Output ? SignalProperties::OUTPUT : SignalProperties::INPUT, std::numeric_limits::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())); + C->setExecutionPolicy(SignalStateDetectorAgents.back(), + DeluxeExecutionPolicy::awaitAny({})); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(SignalStateDetectorAgents.back(), 0, Sensors.back(), SignalConfiguration.Name); C->connectAgents(SystemStateDetectorAgent, SignalStateDetectors.size() - 1, SignalStateDetectorAgents.back(), SignalConfiguration.Name); + + std::ifstream SensorData(SignalConfiguration.InputPath); + if (!SensorData) { + LOG_ERROR_STREAM << "Cannot open Input File \"" + << SignalConfiguration.InputPath << "\" for Signal \"" + << SignalConfiguration.Name << "\"" << std::endl; + return 3; + } + + C->registerSensorValues(Sensors.back(), csv::CSVIterator(SensorData), + csv::CSVIterator()); } // // For simulation output, create a logger agent writing the output of the // high-level agent into a CSV file. // LOG_INFO("Create a logger agent."); // Create CSV writer. std::ofstream OutputCSV(AppConfig.OutputFilePath); // The agent writes each new input value into a CSV file and produces nothing. using Input = std::pair; using Result = Optional>; using Handler = std::function; std::string Name = "Logger Agent"; AgentHandle LoggerAgent = C->createAgent("Logger Agent", Handler([&OutputCSV](Input I) -> Result { OutputCSV << std::get<0>(I.first) << std::endl; return Result(); })); + C->setExecutionPolicy(LoggerAgent, DeluxeExecutionPolicy::awaitAny({})); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, SystemStateDetectorAgent, "SystemStateDetector Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // // Simulate. // C->simulate(AppConfig.NumberOfSimulationCycles); return 0; } diff --git a/apps/ccam/configuration.h b/apps/ccam/configuration.h index 4db55b4..cec43ad 100644 --- a/apps/ccam/configuration.h +++ b/apps/ccam/configuration.h @@ -1,85 +1,85 @@ #ifndef CONFIGURATION_H #define CONFIGURATION_H // clang-tidy off // clang-format off #include "json.hpp" // clang-format on // clang-tidy on #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include using namespace rosa; using nlohmann::json; struct SignalConfiguration { std::string Name; + std::string InputPath; bool Output; float InnerBound; float OuterBound; float InnerBoundDrift; float OuterBoundDrift; uint32_t SampleHistorySize; uint32_t DABSize; uint32_t DABHistorySize; }; struct AppConfiguration { - std::string InputFilePath; std::string OutputFilePath; uint32_t BrokenCounter; uint32_t NumberOfSimulationCycles; std::vector SignalConfigurations; }; void from_json(const json &J, SignalConfiguration &SC) { J.at("Name").get_to(SC.Name); + J.at("InputPath").get_to(SC.InputPath); J.at("Output").get_to(SC.Output); J.at("InnerBound").get_to(SC.InnerBound); J.at("OuterBound").get_to(SC.OuterBound); J.at("InnerBoundDrift").get_to(SC.InnerBoundDrift); J.at("OuterBoundDrift").get_to(SC.OuterBoundDrift); J.at("SampleHistorySize").get_to(SC.SampleHistorySize); J.at("DABSize").get_to(SC.DABSize); J.at("DABHistorySize").get_to(SC.DABHistorySize); } void from_json(const json &J, AppConfiguration &AC) { - J.at("InputFilePath").get_to(AC.InputFilePath); J.at("OutputFilePath").get_to(AC.OutputFilePath); J.at("BrokenCounter").get_to(AC.BrokenCounter); J.at("NumberOfSimulationCycles").get_to(AC.NumberOfSimulationCycles); J.at("SignalConfigurations").get_to(AC.SignalConfigurations); } AppConfiguration AppConfig; bool readConfigFile(std::string ConfigPath) { LOG_INFO("READING CONFIG FILE"); LOG_INFO_STREAM << "Looking for config file at \"" << ConfigPath << "\"\n"; std::ifstream ConfigFile; ConfigFile.open(ConfigPath); if (!ConfigFile) { LOG_ERROR("Unable to open config file"); return false; } json ConfigObj; ConfigFile >> ConfigObj; LOG_INFO_STREAM << "Read JSON file as \"" << ConfigObj << "\"\n"; try { ConfigObj.get_to(AppConfig); } catch (nlohmann::detail::type_error ex) { LOG_ERROR("Misformatted Config File"); return false; } return true; } #endif // CONFIGURATION_H diff --git a/apps/ccam/statehandlerutils.h b/apps/ccam/statehandlerutils.h index 73f25b0..9bf7ee4 100644 --- a/apps/ccam/statehandlerutils.h +++ b/apps/ccam/statehandlerutils.h @@ -1,186 +1,184 @@ #ifndef STATEHANDLERUTILS_H #define STATEHANDLERUTILS_H #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/agent/FunctionAbstractions.hpp" #include #include #include #include #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 #include #include #include using namespace rosa; using namespace rosa::agent; using namespace rosa::deluxe; using namespace rosa::terminal; // Signal State using SignalStateTuple = DeluxeTuple; + float, uint8_t, uint32_t, uint8_t>; AgentHandle createSignalStateDetectorAgent( std::unique_ptr &C, const std::string &Name, std::shared_ptr< SignalStateDetector> SigSD) { using Input = std::pair, bool>; using Result = Optional; using Handler = std::function; return C->createAgent( Name, Handler([&Name, &SigSD](Input I) -> Result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") << " 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.StateID, StateInfo.SignalProperty, //@benedikt: I changed this // StateInfo.SignalStateConfidence, StateInfo.ConfidenceOfMatchingState, StateInfo.ConfidenceOfMismatchingState, StateInfo.ConfidenceStateIsValid, StateInfo.ConfidenceStateIsInvalid, StateInfo.ConfidenceStateIsStable, StateInfo.ConfidenceStateIsDrifting, StateInfo.StateCondition, StateInfo.NumberOfInsertedSamplesAfterEntrance, - StateInfo.StateIsValid, StateInfo.StateJustGotValid, - StateInfo.StateIsValidAfterReentrance}; + (StateInfo.StateIsValid ? 4 : 0) + + (StateInfo.StateJustGotValid ? 2 : 0) + + (StateInfo.StateIsValidAfterReentrance ? 1 : 0)}; return Result(Res); } return Result(); })); } // System State using SystemStateTuple = DeluxeTuple; template struct Handler_helper; template struct function_helper { static_assert(std::conjunction_v...>, "All types need to be identical"); static B function(A valA, As... valAs) { std::vector ar({valA, valAs...}); return func()(ar); } }; template struct Handler_helper<0, ret, functype, typeA, B...> { using handler = function_helper; }; template struct Handler_helper { using handler = typename Handler_helper, B...>::handler; }; template using Handler = typename Handler_helper::handler; // todo: state-detector durschleifen template struct function { ret operator()(A a) { // std::vector 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>; template AgentHandle createSystemStateDetectorAgent( std::unique_ptr &C, const std::string &Name, std::shared_ptr> BrokenDelayFunction, std::shared_ptr> OkDelayFunction) { LOG_TRACE("Creating fixed SystemStateDetectorAgent"); using Input = SignalStateTuple; using Result = Optional; auto HandlerFunction = Handler, arr>, Input>::function; std::shared_ptr< SystemStateDetector> - //@benedikt: I had to insert a dummy number for NumberOfSignals (I - // inserted 5) - SysSD( - new SystemStateDetector( - std::numeric_limits::max(), 5, BrokenDelayFunction, - OkDelayFunction)); + SysSD(new SystemStateDetector( + std::numeric_limits::max(), NumOfSlaves, BrokenDelayFunction, + OkDelayFunction)); return C->createAgent(Name, std::function(HandlerFunction)); } AgentHandle createSystemStateDetectorAgent( std::unique_ptr &C, const std::string &Name, size_t NumOfSlaves, std::shared_ptr> BrokenDelayFunction, std::shared_ptr> OkDelayFunction) { LOG_TRACE("Creating dynamic SystemStateDetectorAgent"); 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/SystemStateDetector.hpp b/include/rosa/agent/SystemStateDetector.hpp index 9152dfa..bcff7b4 100644 --- a/include/rosa/agent/SystemStateDetector.hpp +++ b/include/rosa/agent/SystemStateDetector.hpp @@ -1,223 +1,222 @@ //===-- 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/SignalState.hpp" #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. // TODO: think about shifting this in SystemStateDetector and make a // StateCondition with only stable, drifting, unknown 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 class SystemStateDetector : public StateDetector { using StateDetector = StateDetector; using PartFuncPointer = typename StateDetector::PartFuncPointer; private: // For the convinience to write a shorter data type name using SystemStatePtr = std::shared_ptr>; /// TODO: description uint32_t NumberOfSignals; /// 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 DetectedSystemStates; /// TODO: description unsigned int TimeOfDisparity; /// 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, uint32_t NumberOfSignals, PartFuncPointer FuzzyFunctionDelayTimeToGetBroken, PartFuncPointer FuzzyFunctionDelayTimeToBeWorking) noexcept - : CurrentSystemState(nullptr), - DetectedSystemStates(MaximumNumberOfSystemStates), - NumberOfSignals(NumberOfSignals), TimeOfDisparity(0), + : NumberOfSignals(NumberOfSignals), CurrentSystemState(nullptr), + DetectedSystemStates(MaximumNumberOfSystemStates), TimeOfDisparity(0), FuzzyFunctionDelayTimeToGetBroken(FuzzyFunctionDelayTimeToGetBroken), FuzzyFunctionDelayTimeToBeWorking(FuzzyFunctionDelayTimeToBeWorking) { //@Benedikt: if I write "NextStateID(1), StateHasChanged(false)" before the //{}-brackets, the compiler tells me: "SystemStateDetector.hpp:72:9: error: // member initializer 'NextStateID'/'StateHasChanged' does not name a // non-static data member or base class" this->NextStateID = 1; this->StateHasChanged = false; } /// Destroys \p this object. ~SystemStateDetector(void) = default; /// TODO: write description SystemStateInformation detectSystemState(std::vector> SignalStateInfos) noexcept { SystemStateInformation SystemStateInfo; if (!CurrentSystemState) { ASSERT(DetectedSystemStates.empty()); SystemStatePtr S = createNewSystemState(); CurrentSystemState = S; SystemStateInfo = CurrentSystemState->insertSignalStateInformation(SignalStateInfos); } else { SystemStateRelation SysStateRel = CurrentSystemState->compareSignalStateInformation(SignalStateInfos); if (SysStateRel == SystemStateRelation::STATEISMATCHING) { TimeOfDisparity = 0; SystemStateInfo = CurrentSystemState->insertSignalStateInformation(SignalStateInfos); } else { // ONLYINPUTISMATCHING, ONLYOUTPUTISMATCHING, STATEISMISMATCHING if (!CurrentSystemState->systemStateInformation().StateIsValid) DetectedSystemStates.deleteEntry(CurrentSystemState); CurrentSystemState = nullptr; SystemStatePtr potentialSystemState = nullptr; // search all saved system states for (auto &SavedSystemState : DetectedSystemStates) { SysStateRel = SavedSystemState->compareSignalStateInformation(SignalStateInfos); if (SysStateRel == SystemStateRelation::STATEISMATCHING) { CurrentSystemState = SavedSystemState; break; } else if (SysStateRel == SystemStateRelation::ONLYINPUTISMATCHING || SysStateRel == SystemStateRelation::ONLYOUTPUTISMATCHING) { potentialSystemState = SavedSystemState; } } // actions depending whether state is matchin fully or only half if (CurrentSystemState) { TimeOfDisparity = 0; SystemStateInfo = CurrentSystemState->insertSignalStateInformation( SignalStateInfos); } else if (potentialSystemState) { TimeOfDisparity++; CurrentSystemState = potentialSystemState; SystemStateInfo = CurrentSystemState->systemStateInformation(); } else { SystemStatePtr S = createNewSystemState(); TimeOfDisparity = 0; CurrentSystemState = S; SystemStateInfo = CurrentSystemState->insertSignalStateInformation( SignalStateInfos); } } } // TODO: is this right? if i don't insert if broke, it will never be valid?! // right? if (!SystemStateInfo.StateIsValid || !SystemStateInfo.StateIsValidAfterReentrance) { TimeOfDisparity = 0; } // TODO: maybe make reference instead of pointer CONFDATATYPE ConfidenceSystemIsMalfunctioning = FuzzyFunctionDelayTimeToGetBroken->operator()( static_cast(TimeOfDisparity)); CONFDATATYPE ConfidenceSystemIsFunctioning = FuzzyFunctionDelayTimeToBeWorking->operator()( static_cast(TimeOfDisparity)); if (ConfidenceSystemIsMalfunctioning > ConfidenceSystemIsFunctioning) SystemStateInfo.StateCondition = StateConditions::MALFUNCTIONING; // TODO: calculate overall confidence if (SystemStateInfo.StateJustGotValid) { this->NextStateID++; } return SystemStateInfo; } private: /// Creates a new system state and adds it to the system state vector in /// which /// all known states are saved. /// /// \return a pointer to the newly created signal state or NULL if no state /// could be created. SystemStatePtr createNewSystemState(void) noexcept { SystemStatePtr S(new SystemState( this->NextStateID, this->NumberOfSignals)); DetectedSystemStates.addEntry(S); return S; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_SYSTEMSTATEDETECTOR_HPP diff --git a/include/rosa/support/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp index c5678ef..3b6ed7b 100755 --- a/include/rosa/support/csv/CSVReader.hpp +++ b/include/rosa/support/csv/CSVReader.hpp @@ -1,484 +1,484 @@ //===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVReader.hpp /// /// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger /// (edwin.willegger@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facitilities to read CSV files. /// /// \note The implementation is based on the solution at /// https://stackoverflow.com/a/1120224 /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVREADER_HPP #define ROSA_SUPPORT_CSV_CSVREADER_HPP #include "rosa/support/debug.hpp" #include "rosa/support/sequence.hpp" #include #include #include #include #include #include namespace rosa { namespace csv { /// Indicating it the CSV file contains any header or not enum class HeaderInformation { HasHeader, HasNoHeader }; /// Anonymous namespace providing implementation details for /// \c rosa::csv::CSVIterator, consider it private. namespace { /// Provides facility for parsing one value from a string. /// /// \tparam T type of value to parse /// \tparam IsSignedInt if \p T is a signed integral type, always use default /// \tparam IsUnsignedInt if \p T is an unsigned integral type, always use /// default /// \tparam IsFloat if \p T is a floating-point type, always use default /// \tparam IsString if \p T is \c std::string, always use default /// /// \note Specializations of this struct are provided for arithmentic types /// and \c std::string. template ::value && std::is_signed::value), bool IsUnsignedInt = (std::is_integral::value && std::is_unsigned::value), bool IsFloat = std::is_floating_point::value, bool IsString = std::is_same::value> struct ValueParser { /// /// /// \param Cell the \c std::string to parse /// /// \return the parsed value /// /// \note The function silently fails if cannot parse \p Cell for type \p T. static T parse(const std::string &Cell) noexcept; }; template struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_signed::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stoll(Cell)); } }; template struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_unsigned::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stoull(Cell)); } }; template struct ValueParser { STATIC_ASSERT((std::is_floating_point::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stold(Cell)); } }; template struct ValueParser { STATIC_ASSERT((std::is_same::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return Cell; } }; /// Parses and stores entries from a row of CSV data. /// /// \tparam Ts types of values to parse and store, i.e. entries in the row /// /// \note The implementation relies on \c rosa::csv::CSVRowParser, which is /// implemented only for `arithmetic` types -- signed and unsigned integral /// and floating-point types -- and for \c std::string. Those are the valid /// values for \p Ts. template class CSVRow { private: /// Parses a given row of CSV data into \c CSVRow::Data. /// /// \ CSVRow::Data is filled with values parsed from \p LineStream. Entries /// in the line are to be separated by commas, the character `,`. /// /// \note Parsed values are silently converted to types \p Ts. /// /// \note Parsing silently fails if values do not match \p Ts. /// /// \tparam S0 indices to access tuple elements. /// /// \param [in,out] LineStream the line to parse /// /// \note The last argument is used only to get \p S0, the actual value of /// the parameter is ignored. template - void parseRow(std::stringstream &LineStream, char Delimeter, Seq) { + void parseRow(std::stringstream &LineStream, char Delimiter, Seq) { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "Not matching template arguments."); std::string Cell; // Get fields and parse the values into the proper element of the tuple // one by one in a fold expression. - ((std::getline(LineStream, Cell, Delimeter), + ((std::getline(LineStream, Cell, Delimiter), std::get(Data) = ValueParser::parse(Cell)), ...); } public: /// Constructor with all possible parameters /// /// The function creates an instance of an CSVRow object and sets the /// attributes of the /// object to the values of the parameters. /// /// \param SkipRows the number of data rows to skip, not taking header into /// account. /// \param HeaderInfo is the first line of the file a header row or not. - /// \param Delimeter to seperate between the data entries within one row. + /// \param Delimiter to seperate between the data entries within one row. CSVRow(const size_t SkipRows = 0, const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, - const char Delimeter = ',') - : SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), + const char Delimiter = ',') + : SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimiter(Delimiter), RowNumber(0), IsHeaderRead(false) {} /// Parses and stores one row of CSV data. /// /// The function reads one line from \p Str and parses it into /// \c rosa::csv::CSVRow::Data using \c rosa::csv::CSVRowParser. /// /// \param [in,out] Str input stream of a CSV file void readNextRow(std::istream &Str) noexcept { std::string Line; std::getline(Str, Line); if (Line.size() > 0) { std::stringstream LineStream(Line); - parseRow(LineStream, Delimeter, seq_t()); + parseRow(LineStream, Delimiter, seq_t()); RowNumber = RowNumber + 1; } } /// Read header row and stores it as \p std::string. /// /// The function reads the first line of the csv file and stores the entries /// in a vector. /// /// \param [in,out] Str input stream of a CSV file void readHeader(std::istream &Str) noexcept { std::string Line; std::getline(Str, Line); std::stringstream LineStream(Line); std::string Value; - while (getline(LineStream, Value, Delimeter)) { + while (getline(LineStream, Value, Delimiter)) { Header.push_back(Value); } IsHeaderRead = true; } /// The number of rows to skip once. /// /// This function returns the number of data rows to skip /// at the beginning of the file. /// /// \return The number of rows to skip at the beginning of a csv file. inline size_t SkipNumRows() const noexcept { return this->SkipRows; } /// The current row number within the csv file. /// /// This function returns the current row number. The header /// row is not counted as a row. /// /// \returns the current row number within the csv file. inline size_t CurRow() const noexcept { return this->RowNumber; } /// Indiciates if the header was already read. /// /// This function returns true, if the header of a csv file which contains /// a header file is already read. /// The user has to pass in the attribute HeaderInfo the information if the /// file has in the first row the header row or not. /// /// \return if the header of a file is already read. inline bool IsHeaderReadDone() const noexcept { return this->IsHeaderRead; } /// Indicates if the file contains a header row in the first row. /// /// This function returns if the file contains a header row. /// The information if the file contains a header row or not, has to be passed /// by the user. /// The standard value is HeaderInformation::HasHeader /// /// \return if the csv file contains a header row in the first line of the /// file. inline HeaderInformation HasFileHeader() const noexcept { return this->HeaderInfo; } /// Set the number of rows to skip. /// /// This function sets the number of rows to skip at the beginning of /// the reading of the file. /// - /// \param SkipRows the number of rows you want to skip at the beginning of + /// \param _SkipRows the number of rows you want to skip at the beginning of /// the file. // NOTE (Maxi): I had to change "SkipRows" to "_SkipRows" because otherwise: // "warning: C4458: declaration of 'SkipRows' hides class member" inline void SetSkipRows(const size_t _SkipRows) noexcept { this->SkipRows = _SkipRows; } /// Is the first row a header row or not. /// /// This function sets the information, if the first row of the csv file /// is a header line or not. /// - /// \param HeaderInfo if the first row is a header row or not. + /// \param _HeaderInfo if the first row is a header row or not. // NOTE (Maxi): I had to change "HeaderInfo" to "_HeaderInfo", otherwise: // "warning: C4458: declaration of 'HeaderInfo' hides class member" inline void SetHeaderInfo(const HeaderInformation _HeaderInfo) noexcept { this->HeaderInfo = _HeaderInfo; } /// Set the seperator between data entries. /// /// This funcction sets the separator between the data entries of the csv /// file. /// - /// \param Delimeter the character that separates the data values. - // NOTE (Maxi): I had to change "Delimeter" to "_Delimeter" because otherwise: - // "warning: C4458: declaration of 'Delimeter' hides class member" - inline void SetDelimeter(char _Delimeter) { this->Delimeter = _Delimeter; } + /// \param _Delimiter the character that separates the data values. + // NOTE (Maxi): I had to change "Delimiter" to "_Delimiter" because otherwise: + // "warning: C4458: declaration of 'Delimiter' hides class member" + inline void SetDelimiter(char _Delimiter) { this->Delimiter = _Delimiter; } /// Gives a constant references for the \c std::tuple containing the values /// read by \p this object. /// /// \return \c CSVRow::Data const std::tuple &tuple(void) const noexcept { return Data; } private: std::tuple Data; ///< Stores parsed entries size_t SkipRows; ///< The number of rows to skip at the very beginning of the /// file. ///< This number only applies on the number of data rows. ///< If your file contains a header row and data rows, the skiping ///< of the header row is not taken into account. HeaderInformation HeaderInfo; ///< If the file contains a header row or not. - char Delimeter; ///< The seperator between the data entries. + char Delimiter; ///< The seperator between the data entries. size_t RowNumber; ///< Current row number, counts all row numbers including /// the header row. bool IsHeaderRead; ///< Was the header read or not. std::vector Header; ///< The content of the header row. }; /// Reads a row of CSV data into \c rosa::csv::CSVRow. /// /// The next line is read from \p Str by calling /// \c rosa::csv::CSVRow::readNextRow on \p Data until all lines are /// skipped. /// /// If the function is called for the first time and the file contains /// a header than is the header and the first data row read in after the /// number of rows that the user wants to skip. /// /// \tparam Ts type of values to read from the row /// /// \note The CSV file should contain a line with fields matching \p Ts... /// /// \param [in,out] Str input stream of a CSV file /// \param [in,out] Data object to read the next line into /// /// \return \p Str after reading one line from it template std::istream &operator>>(std::istream &Str, CSVRow &Data) { if (Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone()) { Data.readHeader(Str); } while (Data.CurRow() < (Data.SkipNumRows())) { Data.readNextRow(Str); } // read the lines after you skipped the number of rows you want to skip Data.readNextRow(Str); return Str; } } // End namespace /// Provides `InputIterator` features for iterating over a CSV file. /// /// The iterator parses rows into `std::tuple` values and iterates over the /// file row by row. /// /// \tparam Ts types of values stored in one row of the CSV file /// /// \note The iterator expects each row to consists of fields matching \p Ts. /// /// \note The implementation relies on \c rosa::csv::CSVRow, which in turn /// relies on \c rosa::csv::CSVRowParser, which is implemented only for /// `arithmetic` types -- signed and unsigned integral types and floating-point /// types -- and for \c std::string. Those are the valid values for \p Ts template class CSVIterator { public: /// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator /// /// Standard `typedef`s for iterators. /// ///@{ typedef std::input_iterator_tag iterator_category; ///< Category of the iterator. typedef std::tuple value_type; ///< Type of values iterated over. typedef std::size_t difference_type; ///< Type to identify distance. typedef std::tuple *pointer; ///< Pointer to the type iterated over. typedef std::tuple &reference; ///< Reference to the type iterated over. ///@} /// Creates a new instance. /// /// \param [in,out] S input stream to iterate over /// \param SkipRows the number of rows you want to skip only once at /// the beginning of the file. /// If you have an header in the file, it is supposed to be /// the first row, and it will be always read out. /// But after this header the next number of Rows will be /// skipped. /// \param HeaderInfo is used to know wheter the file contains an /// header row or not. /// The header has to be in the first row. - /// \param Delimeter is the separator between the differnt values of + /// \param Delimiter is the separator between the differnt values of /// the csv file. CSVIterator(std::istream &S, const size_t SkipRows = 0, const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, - const char Delimeter = ',') + const char Delimiter = ',') : Str(S.good() ? &S : nullptr), SkipRows(SkipRows), - HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row() { + HeaderInfo(HeaderInfo), Delimiter(Delimiter), Row() { Row.SetSkipRows(SkipRows); Row.SetHeaderInfo(HeaderInfo); - Row.SetDelimeter(Delimeter); + Row.SetDelimiter(Delimiter); // \c rosa::csv::CSVIterator::Row is initialized empty so the first // incrementation here will read the first row. ++(*this); } /// Creates an empty new instance. CSVIterator(void) noexcept : Str(nullptr) {} /// Pre-increment operator. /// /// The implementation reads the next row. If the end of the input stream is /// reached, the operator becomes empty and has no further effect. /// /// \return \p this object after incrementing it. CSVIterator &operator++() { if (Str) { if (!((*Str) >> Row)) { Str = nullptr; } } return *this; } /// Post-increment operator. /// /// The implementation uses the pre-increment operator and returns a copy of /// the original state of \p this object. /// /// \return \p this object before incrementing it. CSVIterator operator++(int) { CSVIterator Tmp(*this); ++(*this); return Tmp; } /// Returns a constant reference to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant reference to the current entry. const std::tuple &operator*(void)const noexcept { return Row.tuple(); } /// Returns a constant pointer to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant pointer to the current entry. const std::tuple *operator->(void)const noexcept { return &Row.tuple(); } /// Tells if \p this object is equal to another one. /// /// Two \c rosa::csv::CSVReader instances are equal if and only if they are /// the same or both are empty. /// /// \param RHS other object to compare to /// /// \return whether \p this object is equal with \p RHS bool operator==(const CSVIterator &RHS) const noexcept { return ((this == &RHS) || ((this->Str == nullptr) && (RHS.Str == nullptr))); } /// Tells if \p this object is not equal to another one. /// /// \see rosa::csv::CSVReader::operator== /// /// \param RHS other object to compare to /// /// \return whether \p this object is not equal with \p RHS. bool operator!=(const CSVIterator &RHS) const noexcept { return !((*this) == RHS); } - /// Set the delimeter used in the csv file. - /// \param Delimeter the character which separates the values in the csv file. - // NOTE (Maxi): I had to change "Delimeter" to "_Delimeter" because otherwise: - // "warning: C4458: declaration of 'Delimeter' hides class member" - inline void setDelimeter(char _Delimeter) noexcept { - this->Delimeter = _Delimeter; + /// Set the delimiter used in the csv file. + /// \param _Delimiter the character which separates the values in the csv file. + // NOTE (Maxi): I had to change "Delimiter" to "_Delimiter" because otherwise: + // "warning: C4458: declaration of 'Delimiter' hides class member" + inline void setDelimiter(char _Delimiter) noexcept { + this->Delimiter = _Delimiter; } - /// get the delimeter currently set to separate the values in the csv file. + /// get the delimiter currently set to separate the values in the csv file. /// \return the current character, which is used to separte teh values in the /// csv file. - inline char getDelimeter() const noexcept { return this->Delimeter; } + inline char getDelimiter() const noexcept { return this->Delimiter; } private: std::istream *Str; ///< Input stream of a CSV file to iterate over. size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the /// file. HeaderInformation HeaderInfo; ///< does the csv file contain a header or not, /// if this information is ///< not given correclty, the reading of the header would result in ///< in an error. - char Delimeter; ///< Delimeter between the entries in the csv file. + char Delimiter; ///< Delimiter between the entries in the csv file. CSVRow Row; ///< Content of the current row }; } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVREADER_HPP diff --git a/include/rosa/support/csv/CSVWriter.hpp b/include/rosa/support/csv/CSVWriter.hpp index a077e3d..f1cf2ac 100755 --- a/include/rosa/support/csv/CSVWriter.hpp +++ b/include/rosa/support/csv/CSVWriter.hpp @@ -1,221 +1,221 @@ //===-- rosa/support/csv/CSVWriter.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVWriter.hpp /// /// \authors David Juhasz (david.juhasz@tuwien.ac.at) /// Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facitilities to write CSV files. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVWRITER_HPP #define ROSA_SUPPORT_CSV_CSVWRITER_HPP #include #include #include #include #include #include "rosa/support/log.h" namespace rosa { namespace csv { /// Provides facilities to write values into a CSV file. /// /// The writer emits a comma, the character `,`, between each written values. /// The resulted stream is a flat CSV file as it consists of onlyone row, no new /// line is emitted. /// /// \tparam T type of values to write template class CSVWriter { public: /// Creates a new instance. /// /// \param [in,out] S output stream to write to /// /// \note The writer operates on non-binary outputs as long as \p S is in /// good state. CSVWriter(std::ostream &S) : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), IsFirst(true) {} /// Tells if the last operation was successful. /// /// \return if the last operation was successful bool good(void) const noexcept { return Str != nullptr; } /// Writes an entry to the output stream. /// /// The implementation does anything only if the last operation was /// successful. If so, \p V is written to \c rosa::csv::CSVWriter::Str. /// The emitted value is preceded with a comma if the actual call is not the /// first one for \p this object. Success of the operation is checked at the /// end. /// /// \param V value to write void write(const T &V) { if (Str) { if (!IsFirst) { *Str << ','; } else { IsFirst = false; } *Str << V; if (!Str->good()) { Str = nullptr; } } } private: std::ostream *Str; ///< Output stream to write to. bool IsFirst; ///< Denotes if the next write would be the first one. }; /// Writes a tuple of values into a CSV file /// /// \tparam Ts types of values to write template class CSVTupleWriter { public: // typedef value_type ; ///< Type of values written. typedef std::tuple value_type; /// Creates a new instance. /// /// \param [in,out] S output stream to write to /// /// \note The writer operates on non-binary outputs as long as \p S is in /// good state. CSVTupleWriter(std::ostream &S) : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), IsHeaderWritten(false), IsDataWritten(false) {} /// Tells if the last operation was successful. /// /// \return if the last operation was successful bool good(void) const noexcept { return Str != nullptr; } /// Write the values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter. /// /// \see rosa::csv::CSVTupleWriter /// /// /// \param [in,out] values tuple, which values are written in a recusive fashion into a stream. template void write(const std::tuple &values) { constexpr size_t size = sizeof...(Ts); LOG_TRACE_STREAM << "Writing tuple values into file \n"; LOG_TRACE_STREAM << " Tuple has " << std::to_string(size) << " elements. \n"; LOG_TRACE_STREAM << " Value is " << std::get(values); if(Str){ /// Write the current element of the tuple into the stream and add a separtor after it, /// and call the function for the next element in the tuple. if constexpr(i+1 != sizeof...(Ts)){ *Str << std::get(values) << ", "; write(values); /// If the last element is written into the stream than begin a new line. }else if constexpr(i + 1 == sizeof...(Ts)){ *Str << std::get(values) << '\n'; /// every time the last data value of a line is written, the flag indicates that data was already written into the file. IsDataWritten = true; } } } /// Write the header values to a CSV file with \c rosa::csv::CSVTupleWriter. /// /// \note The function has no effect if anything has already been written /// to the output stream either by \c /// rosa::csv::CSVTupleWriter::writeHeader() or \c /// rosa::csv::CSVTupleWriter::write(). /// /// \see rosa::csv::CSVTupleWriter /// /// \param header the content of the header line. void writeHeader(const std::array &header){ size_t index = 0; /// write into the stream only, if it is not a nullptr, and if no data and no header was already written into it. if(Str && IsDataWritten == false && IsHeaderWritten == false){ index = 0; for (auto i = header.begin(); i != header.end(); ++i){ index = index + 1; - /// write into the stream every entry with a delimeter, in this case ", " until + /// write into the stream every entry with a delimiter, in this case ", " until /// the last entry if(index != header.size()){ *Str << *i << ", "; - /// write the last entry into the stream, without any delimeter + /// write the last entry into the stream, without any delimiter }else { *Str << *i; } } /// finish the header line and start a new line. *Str << '\n'; /// now it is not possible to write additional header lines. IsHeaderWritten = true; } } private: std::ostream *Str; ///< Output stream to write to. bool IsHeaderWritten; ///< If an header line was already written into the stream. If set than no additional header could be written. bool IsDataWritten; ///< If one line of data has already been written into the stream, than no headerline could be added. }; /// Writes all values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter. /// /// \see rosa::csv::CSVTupleWriter /// /// \tparam Ts types of values to write /// /// \param [in,out] W object to write with /// \param V values to write /// /// \return \p W after writing \p V with it template CSVTupleWriter &operator<<(CSVTupleWriter &W, const std::tuple &V) { W.write(V); return W; } /// Writes a value to a CSV file with \c rosa::csv::CSVWriter. /// /// \see rosa::csv::CSVWriter /// /// \tparam T type of value to write /// /// \param [in,out] W object to write with /// \param V value to write /// /// \return \p W after writing \p V with it template CSVWriter &operator<<(CSVWriter &W, const T& V) { W.write(V); return W; } } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVWRITER_HPP