diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp index e1668e6..983a106 100644 --- a/apps/ccam/ccam.cpp +++ b/apps/ccam/ccam.cpp @@ -1,383 +1,386 @@ //===-- 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" using namespace rosa; using namespace rosa::agent; using namespace rosa::deluxe; using namespace rosa::terminal; const std::string AppName = "CCAM"; //@maxi I don't know what you want to do but if you want to have the agent be a //"lowlevel" agent which gives its master a tuple with all of that info you have // to do it as it is done in the #else. To be fair I don't know if the return // SignalStateTuple(); is allowed +/* #if false using SignalStateTuple = std::tuple, Optional, Optional, Optional, Optional, Optional, Optional, Optional, Optional>; AgentHandle createSignalStateDetectorAgent( std::unique_ptr &C, const std::string &Name, std::shared_ptr< SignalStateDetector> SigSD) { (void)SigSD; using Handler = std::function)>; return C->createAgent( Name, Handler([&Name, &SigSD](std::pair I) -> SignalStateTuple { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; auto StateInfo = SigSD->detectSignalState(I.first); if (I.second) return std::make_tuple( Optional(I.first), Optional(StateInfo.SignalStateID), Optional(StateInfo.SignalStateConfidence), Optional(StateInfo.SignalStateCondition), Optional( StateInfo.NumberOfInsertedSamplesAfterEntrance), Optional(StateInfo.SignalStateIsValid), Optional(StateInfo.SignalStateJustGotValid), Optional(StateInfo.SignalStateIsValidAfterReentrance), Optional(StateInfo.SignalIsStable)); return SignalStateTuple(); })); } #else using tup = DeluxeTuple; using SignalStateTuple = Optional; AgentHandle createSignalStateDetectorAgent( std::unique_ptr &C, const std::string &Name, std::shared_ptr< SignalStateDetector> SigSD) { (void)SigSD; using Handler = std::function, bool>)>; return C->createAgent( Name, Handler([&Name, &SigSD]( std::pair, bool> I) -> SignalStateTuple { 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) + return {tup( std::get<0>(I.first), StateInfo.SignalStateID, StateInfo.SignalStateConfidence, StateInfo.SignalStateCondition, StateInfo.NumberOfInsertedSamplesAfterEntrance, StateInfo.SignalStateIsValid, StateInfo.SignalStateJustGotValid, StateInfo.SignalStateIsValidAfterReentrance, StateInfo.SignalIsStable)}; return SignalStateTuple(); })); } #endif +*/ 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 C = DeluxeContext::create(AppName); 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)); // // 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( 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())); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(SignalStateDetectorAgents.back(), 0, Sensors.back(), "HR Sensor Channel"); } 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)); std::shared_ptr< - SystemStateDetector> + SystemStateDetector> SystemStateDetectorF( - new SystemStateDetector(std::numeric_limits::max(), - BrokenDelayFunction, OkDelayFunction)); + new SystemStateDetector( + std::numeric_limits::max(), BrokenDelayFunction, + OkDelayFunction)); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); // The new agent logs its input values and results in the the sum of them. /** AgentHandle BodyAgent = C->createAgent( "Body Agent", DeluxeAgent::D( [](std::pair HR, std::pair BR, std::pair SpO2, std::pair BPSys, std::pair BodyTemp) -> Optional { LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n" << (HR.second ? "" : "") << " HR warning score: " << HR.first << "\n" << (BR.second ? "" : "") << " BR warning score: " << BR.first << "\n" << (SpO2.second ? "" : "") << " SpO2 warning score: " << SpO2.first << "\n" << (BPSys.second ? "" : "") << " BPSys warning score: " << BPSys.first << "\n" << (BodyTemp.second ? "" : "") << " 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 ScoreWriter(ScoreCSV); // The agent writes each new input value into a CSV file and produces nothing. /** AgentHandle LoggerAgent = C->createAgent( "Logger Agent", DeluxeAgent::D( [&ScoreWriter](std::pair Score) -> Optional { 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/include/rosa/agent/SignalState.hpp b/include/rosa/agent/SignalState.hpp index 9d7cba7..b6149a4 100644 --- a/include/rosa/agent/SignalState.hpp +++ b/include/rosa/agent/SignalState.hpp @@ -1,471 +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 struct SignalStateInformation { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::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; }; /// \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 class SignalState : public Functionality { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "input data type not arithmetic"); STATIC_ASSERT((std::is_arithmetic::value), "confidence data type is not to arithmetic"); STATIC_ASSERT( (std::is_arithmetic::value), "process data type (DAB and Relative Distance) is not to arithmetic"); public: // For the convinience to write a shorter data type name using PartFuncReference = PartialFunction &; using StepFuncReference = StepFunction &; private: /// SignalStateInfo is a struct SignalStateInformation that contains /// information about the current state. SignalStateInformation SignalStateInfo; /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// confidence how good the new sample matches another sample in the sample /// history. PartFuncReference FuzzyFunctionSampleMatches; /// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the /// confidence how bad the new sample matches another sample in the sample /// history. PartFuncReference FuzzyFunctionSampleMismatches; /// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the /// confidence how many samples from the sampe history match the new sample. StepFuncReference FuzzyFunctionNumOfSamplesMatches; /// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives /// the confidence how many samples from the sampe history mismatch the new /// sample. 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. 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). PartFuncReference FuzzyFunctionSignalIsStable; /// SampleHistory is a history in that the last sample values are stored. DynamicLengthHistory SampleHistory; /// DAB is a (usually) small history of the last sample values of which a /// average is calculated if the DAB is full. DynamicLengthHistory DAB; /// DABHistory is a history in that the last DABs (to be exact, the averages /// of the last DABs) are stored. DynamicLengthHistory DABHistory; /// LowestConfidenceMatchingHistory is a history in that the lowest confidence /// for the current sample matches all history samples are saved. DynamicLengthHistory LowestConfidenceMatchingHistory; /// HighestConfidenceMatchingHistory is a history in that the highest /// confidence for the current sample matches all history samples are saved. DynamicLengthHistory HighestConfidenceMismatchingHistory; public: /// 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, 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, - true}, - + 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 insertSample(INDATATYPE Sample) noexcept { validateSignalState(Sample); SampleHistory.addEntry(Sample); DAB.addEntry(Sample); if (DAB.full()) { PROCDATATYPE AvgOfDAB = DAB.template average(); DABHistory.addEntry(AvgOfDAB); DAB.clear(); } FuzzyFunctionNumOfSamplesMatches.setRightLimit( static_cast(SampleHistory.numberOfEntries())); FuzzyFunctionNumOfSamplesMismatches.setRightLimit( static_cast(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 RelativeDistanceHistory(SampleHistory.maxLength()); // calculate distances to all history samples for (auto &HistorySample : SampleHistory) { PROCDATATYPE RelativeDistance = relativeDistance(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 // 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 ConfidenceFromRelativeDistance = 0; } else { ConfidenceFromRelativeDistance = FuzzyFunctionSampleMatches(RelativeDistanceHistory[Case]); } ConfidenceOfWorstFittingSample = fuzzyAND(ConfidenceOfWorstFittingSample, ConfidenceFromRelativeDistance); ConfidenceOfBestCase = fuzzyOR(ConfidenceOfBestCase, fuzzyAND(ConfidenceOfWorstFittingSample, FuzzyFunctionNumOfSamplesMatches( static_cast(Case) + 1))); } 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 RelativeDistanceHistory(SampleHistory.maxLength()); // calculate distances to all history samples for (auto &HistorySample : SampleHistory) { RelativeDistanceHistory.addEntry( relativeDistance(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; // 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 { ConfidenceFromRelativeDistance = FuzzyFunctionSampleMismatches(RelativeDistanceHistory[Case]); } ConfidenceOfBestFittingSample = fuzzyOR(ConfidenceOfBestFittingSample, ConfidenceFromRelativeDistance); ConfidenceOfWorstCase = fuzzyAND(ConfidenceOfWorstCase, fuzzyOR(ConfidenceOfBestFittingSample, FuzzyFunctionNumOfSamplesMismatches( static_cast(Case) + 1))); } return ConfidenceOfWorstCase; } /// Gives information about the current signal state. /// /// \return a struct SignalStateInformation that contains information about /// the current signal state. SignalStateInformation 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. LowestConfidenceMatching = fuzzyAND( LowestConfidenceMatching, FuzzyFunctionSampleMatches(relativeDistance( Sample, HistorySample))); HighestConfidenceMismatching = fuzzyOR(HighestConfidenceMismatching, FuzzyFunctionSampleMismatches( relativeDistance( Sample, HistorySample))); } LowestConfidenceMatchingHistory.addEntry(LowestConfidenceMatching); HighestConfidenceMismatchingHistory.addEntry(HighestConfidenceMismatching); LowestConfidenceMatching = LowestConfidenceMatchingHistory.lowestEntry(); HighestConfidenceMismatching = HighestConfidenceMismatchingHistory.highestEntry(); CONFDATATYPE ConfidenceSignalStateIsValid = fuzzyAND(LowestConfidenceMatching, FuzzyFunctionNumOfSamplesMatches(static_cast( SignalStateInfo.NumberOfInsertedSamplesAfterEntrance))); CONFDATATYPE ConfidenceSignalStateIsInvalid = fuzzyOR(HighestConfidenceMismatching, FuzzyFunctionNumOfSamplesMismatches(static_cast( 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) { ConfidenceSignalIsStable = FuzzyFunctionSignalIsStable( relativeDistance( DABHistory[DABHistory.numberOfEntries() - 1], DABHistory[0])); ConfidenceSignalIsDrifting = FuzzyFunctionSignalIsDrifting( relativeDistance( DABHistory[DABHistory.numberOfEntries() - 1], DABHistory[0])); } else { // @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; } //@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/SystemState.hpp b/include/rosa/agent/SystemState.hpp index 7033135..d245aaf 100644 --- a/include/rosa/agent/SystemState.hpp +++ b/include/rosa/agent/SystemState.hpp @@ -1,97 +1,123 @@ //===-- 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 +#include 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 { STABLE, ///< The system state is stable DRIFTING, ///< The system state is drifting MALFUNCTIONING, ///< The system state is malfunctioning UNKNOWN ///< The system state is unknown }; /// TODO: write description template struct SystemStateInformation { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::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: do we need PROCDATATYPE? /// TODO TEXT -template +template class SystemState : public Functionality { // Make sure the actual type arguments are matching our expectations. + STATIC_ASSERT(std::is_arithmetic::value, + "input data type is not to arithmetic"); STATIC_ASSERT(std::is_arithmetic::value, "confidence abstraction type is not to arithmetic"); + STATIC_ASSERT(std::is_arithmetic::value, + "process data type is not to arithmetic"); private: - // TODO: vector - std::array, - NUMOFINPUTSIGNALS> - InputSignalStates; - std::array, - NUMOFOUTPUTSIGNALS> - OutputSignalStates; + SystemStateInformation SystemStateInfo; + + std::vector> Signals; 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} { + Signals.resize(NumberOfSignals); + } + + /// Destroys \p this object. + ~SystemState(void) = default; + + /// TODO: describe + template + CONFDATATYPE fuzzyOR(const std::array &Data) noexcept { + STATIC_ASSERT(std::is_arithmetic::value, + "Type of FuzzyAnd is not arithmetic"); + STATIC_ASSERT(size > 1, "Number of Arguments is to little"); + ASSERT(std::all_of(Data.begin(), Data.end(), + [](const auto &v) { return v <= 1 && v >= 0; })); + return *std::max_element(Data.begin(), Data.end()); + } + + /// TODO: describe + template + std::enable_if_t< + std::conjunction_v...>, + CONFDATATYPE> + fuzzyOR(const CONFDATATYPE Data, const _CONFDATATYPE... Datan) noexcept { + return fuzzyOR( + std::array{Data, Datan...}); + } }; } // 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 a255e0b..d18607c 100644 --- a/include/rosa/agent/SystemStateDetector.hpp +++ b/include/rosa/agent/SystemStateDetector.hpp @@ -1,106 +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 { // todo: löschen , std::size_t NUMOFINPUTSIGNALS, std::size_t NUMOFOUTPUTSIGNALS /// TODO: write description template + HistoryPolicy HP> class SystemStateDetector : public StateDetector { //@maxi added them to make it compilable is this what you wanted? using StateDetector = StateDetector; using PartFuncPointer = typename StateDetector::PartFuncPointer; private: // For the convinience to write a shorter data type name using SystemStatePtr = - std::shared_ptr>; + std::shared_ptr>; /// 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 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 detectSystemState(INDATATYPE Sample) noexcept { // dummy line Sample = 1; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_SYSTEMSTATEDETECTOR_HPP