diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp index e11ff21..a2488dd 100644 --- a/apps/ccam/ccam.cpp +++ b/apps/ccam/ccam.cpp @@ -1,617 +1,620 @@ //===-- apps/ccam/ccam.cpp --------------------------------------*- C++ -*-===// // // The RoSA Framework -- Application CCAM // // Distributed under the terms and conditions of the Boost Software /// License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file 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 /// /// \todo Clean up source files of this app: add standard RoSA header comment /// for own files and do something with 3rd party files... //===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/agent/DistanceMetrics.hpp" #include "rosa/agent/FunctionAbstractions.hpp" #include #include "rosa/config/version.h" #include "rosa/agent/SignalStateDetector.hpp" #include "rosa/agent/SystemStateDetector.hpp" #include "rosa/app/Application.hpp" #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include "rosa/support/mqtt/MQTTReader.hpp" #include "rosa/app/AppTuple.hpp" #include #include #include #include #include #include "configuration.h" #include "statehandlerutils.h" using namespace rosa; using namespace rosa::agent; using namespace rosa::app; using namespace rosa::terminal; using namespace rosa::mqtt; const std::string AppName = "CCAM"; int main(int argc, char **argv) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << AppName << "app" << Color::Default << '\n'; // // Read the filepath of the config file of the observed system. The filepath // is in the first argument passed to the application. Fuzzy functions etc. // are described in this file. // if (argc < 2) { LOG_ERROR("Specify config File!\nUsage:\n\tccam config.json"); return 1; } std::string ConfigPath = argv[1]; // // Load config file and read in all parameters. Fuzzy functions etc. are // described in this file. // if (!readConfigFile(ConfigPath)) { LOG_ERROR_STREAM << "Could not read config from \"" << ConfigPath << "\"\n"; return 2; } // // Create a CCAM context. // LOG_INFO("Creating Context"); std::unique_ptr AppCCAM = Application::create(AppName); // // Create following function which shall give information if the time gap // between changed input(s) and changed output(s) shows already a malfunction // of the system. // // ____________ // / // / // __________/ // 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)); // // Create following function which shall give information if the time gap // between changed input(s) and changed output(s) still shows a // well-functioning system. // // ____________ // \ // \ // \__________ // 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 AppAgent with SystemStateDetector functionality. // LOG_INFO("Create SystemStateDetector agent."); AgentHandle SystemStateDetectorAgent = createSystemStateDetectorAgent( AppCCAM, "SystemStateDetector", AppConfig.SignalConfigurations.size(), BrokenDelayFunction, OkDelayFunction); // // Set policy of SystemStateDetectorAgent that it wait for all // SignalStateDetectorAgents // std::set pos; for (size_t i = 0; i < AppConfig.SignalConfigurations.size(); ++i) pos.insert(pos.end(), i); AppCCAM->setExecutionPolicy(SystemStateDetectorAgent, AppExecutionPolicy::awaitAll(pos)); // // Create Vectors for all sensors, all signal related fuzzy functions, all // signal state detectors, all signal state agents, and all input data files. // LOG_INFO("Creating sensors, SignalStateDetector functionalities and their " "Abstractions."); std::vector Sensors; std::vector, float>>> DistanceMetrics; std::vector>> SampleMatchesFunctions; std::vector>> SampleMismatchesFunctions; std::vector>> SignalIsStableFunctions; std::vector>> SignalIsDriftingFunctions; // SAVE CHANGES std::vector>> SignalConditionLookBackFunctions; std::vector>> SignalConditionHistoryDesicionFunctions; // - SAVE CHANGES std::vector>> NumOfSamplesMatchFunctions; std::vector>> NumOfSamplesMismatchFunctions; std::vector>> SampleValidFunctions; std::vector>> SampleInvalidFunctions; std::vector>> NumOfSamplesValidFunctions; std::vector>> NumOfSamplesInvalidFunctions; std::vector>> SignalStateDetectors; std::vector SignalStateDetectorAgents; std::vector DataFiles; // // Go through all signal state configurations (number of signals), and create // functionalities for SignalStateDetector. // for (auto SignalConfiguration : AppConfig.SignalConfigurations) { // // Create application sensors. // Sensors.emplace_back( AppCCAM->createSensor(SignalConfiguration.Name + "_Sensor")); // // Create following function(s) which shall give information whether one // sample matches another one (based on the relative distance between them). // // ____________ // / \ // / \ // __________/ \__________ // // if (std::strcmp(SignalConfiguration.DistanceMetric.c_str(), "absolute") == 0 ) { DistanceMetrics.emplace_back(new AbsoluteDistance()); } else if (std::strcmp(SignalConfiguration.DistanceMetric.c_str(), "daniel1") == 0 ) { DistanceMetrics.emplace_back(new DanielsDistance1()); } else if (std::strcmp(SignalConfiguration.DistanceMetric.c_str(), "daniel2") == 0 ) { DistanceMetrics.emplace_back(new DanielsDistance2()); } else if (std::strcmp(SignalConfiguration.DistanceMetric.c_str(), "daniel3") == 0 ) { DistanceMetrics.emplace_back(new DanielsDistance3()); } else if (std::strcmp(SignalConfiguration.DistanceMetric.c_str(), "maxi1") == 0 ) { DistanceMetrics.emplace_back(new MaxisDistance1()); } else { //default is relative distance DistanceMetrics.emplace_back(new RelativeDistance()); } 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)); // // Create following function(s) which shall give information whether one // sample mismatches another one (based on the relative distance between // them). // // ____________ ____________ // \ / // \ / // \__________/ // // 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)); // // Create following function(s) which shall give information whether a // signal is stable. // // ____________ // / \ // / \ // __________/ \__________ // // 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)); // // Create following function(s) which shall give information whether a // signal is drifting. // // ____________ ____________ // \ / // \ / // \__________/ // // 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)); // SAVE CHANGES SignalConditionLookBackFunctions.emplace_back( new PartialFunction( {{{1, SignalConfiguration.DriftLookbackRange + 1}, std::make_shared>( 1, 1.f, SignalConfiguration.DriftLookbackRange + 1, 0.f)}, {{SignalConfiguration.DriftLookbackRange + 1, std::numeric_limits::max()}, std::make_shared>(0.f, 0.f)}}, 1.f)); SignalConditionHistoryDesicionFunctions.emplace_back( new PartialFunction( {{{0, SignalConfiguration.DriftLookbackRange}, std::make_shared>( 0, 0.f, SignalConfiguration.DriftLookbackRange, 1.f)}, {{SignalConfiguration.DriftLookbackRange, std::numeric_limits::max()}, std::make_shared>(1.f, 0.f)}}, 0.f)); // - SAVE CHANGES // // Create following function(s) which shall give information how many // history samples match another sample. // // ____________ // / // / // __________/ // NumOfSamplesMatchFunctions.emplace_back(new StepFunction( 1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepUp)); // // Create following function(s) which shall give information how many // history samples mismatch another sample. // // ____________ // \ // \ // \__________ // NumOfSamplesMismatchFunctions.emplace_back(new StepFunction( 1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepDown)); // // Create following function(s) which shall give information how good all // samples in a state match each other. // // ____________ // / \ // / \ // __________/ \__________ // // SampleValidFunctions.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)); // // Create following function(s) which shall give information how good all // samples in a state mismatch each other. // // ____________ ____________ // \ / // \ / // \__________/ // // SampleInvalidFunctions.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)); // // Create following function(s) which shall give information how many // history samples match each other. // // ____________ // / // / // __________/ // NumOfSamplesValidFunctions.emplace_back(new StepFunction( 1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepUp)); // // Create following function(s) which shall give information how many // history samples mismatch each other. // // ____________ // \ // \ // \__________ // NumOfSamplesInvalidFunctions.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(), DistanceMetrics.back(), SampleMatchesFunctions.back(), SampleMismatchesFunctions.back(), NumOfSamplesMatchFunctions.back(), NumOfSamplesMismatchFunctions.back(), SampleValidFunctions.back(), SampleInvalidFunctions.back(), NumOfSamplesValidFunctions.back(), NumOfSamplesInvalidFunctions.back(), SignalIsDriftingFunctions.back(), SignalIsStableFunctions.back(), // SAVE CHANGES SignalConditionLookBackFunctions.back(), SignalConditionHistoryDesicionFunctions.back(), SignalConfiguration.DriftLookbackRange, // - SAVE CHANGES SignalConfiguration.SampleHistorySize, SignalConfiguration.DABSize, SignalConfiguration.DABHistorySize)); // // Create low-level application agents // SignalStateDetectorAgents.push_back(createSignalStateDetectorAgent( AppCCAM, SignalConfiguration.Name, SignalStateDetectors.back())); AppCCAM->setExecutionPolicy( SignalStateDetectorAgents.back(), AppExecutionPolicy::decimation(AppConfig.DownsamplingRate)); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); AppCCAM->connectSensor(SignalStateDetectorAgents.back(), 0, Sensors.back(), SignalConfiguration.Name + "_Sensor ->" + SignalConfiguration.Name + "_SignalStateDetector_Agent-Channel"); AppCCAM->connectAgents( SystemStateDetectorAgent, SignalStateDetectors.size() - 1, SignalStateDetectorAgents.back(), SignalConfiguration.Name + "_SignalStateDetector_Agent->SystemStateDetector_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 OutputCSV(AppConfig.OutputFilePath); //for (auto SignalConfiguration : AppConfig.SignalConfigurations) { // OutputCSV << SignalConfiguration.Name + ","; //} //OutputCSV << "StateID,"; //OutputCSV << "Confidence State Valid,"; //OutputCSV << "Confidence State Invalid,"; //OutputCSV << "Confidence Inputs Matching,"; //OutputCSV << "Confidence Outputs Matching,"; //OutputCSV << "Confidence Inputs Mismatching,"; //OutputCSV << "Confidence Outputs Mismatching,"; OutputCSV << "State Condition,"; //OutputCSV << "Confidence System Functioning,"; //OutputCSV << "Confidence System Malfunctioning,"; - //OutputCSV << "Overall Confidence,"; + OutputCSV << "Confidence Drift,"; + OutputCSV << "Confidence Drift Up,"; + OutputCSV << "Confidence Drift Down,"; + OutputCSV << "Overall Confidence,"; OutputCSV << "\n"; // 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 = AppCCAM->createAgent( "Logger Agent", Handler([&OutputCSV](Input I) -> Result { const SystemStateTuple &T = I.first; OutputCSV << std::get<0>( static_cast &>(T)) << std::endl; return Result(); })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); AppCCAM->connectAgents(LoggerAgent, 0, SystemStateDetectorAgent, "SystemStateDetector Channel"); // // Only log if the SystemStateDetector actually ran // AppCCAM->setExecutionPolicy(LoggerAgent, AppExecutionPolicy::awaitAll({0})); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize application for simulation. // AppCCAM->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // Make sure DataFiles will not change capacity while adding elements to it. // Changing capacity moves elements away, which invalidates references // captured by CSVIterator. DataFiles.reserve(AppConfig.SignalConfigurations.size()); uint32_t i = 0; bool hasMQTT = false; for (auto SignalConfiguration : AppConfig.SignalConfigurations) { switch (SignalConfiguration.DataInterfaceType) { case DataInterfaceTypes::CSV: DataFiles.emplace_back(SignalConfiguration.InputPath); if (!DataFiles.at(i)) { LOG_ERROR_STREAM << "Cannot open Input File \"" << SignalConfiguration.InputPath << "\" for Signal \"" << SignalConfiguration.Name << "\"" << std::endl; return 3; } AppCCAM->registerSensorValues(Sensors.at(i), csv::CSVIterator(DataFiles.at(i)), csv::CSVIterator()); LOG_INFO_STREAM << "Sensor " << SignalConfiguration.Name << " is fed by csv file " << SignalConfiguration.InputPath << std::endl; break; case DataInterfaceTypes::MQTT: { hasMQTT = true; auto it = MQTTIterator(SignalConfiguration.MQTTTopic); AppCCAM->registerSensorValues(Sensors.at(i), std::move(it), MQTTIterator()); LOG_INFO_STREAM << "Sensor " << SignalConfiguration.Name << " is fed by MQTT topic " << SignalConfiguration.MQTTTopic << std::endl; break; } default: LOG_ERROR_STREAM << "No data source for " << SignalConfiguration.Name << std::endl; break; } i++; } // // Start simulation. // auto &log = LOG_WARNING_STREAM; log << "Simulation starting."; if (hasMQTT) { log << " Publishing MQTT messages may start."; } log << std::endl; AppCCAM->simulate(AppConfig.NumberOfSimulationCycles); return 0; } diff --git a/apps/ccam/statehandlerutils.h b/apps/ccam/statehandlerutils.h index dc40d6c..60620d4 100644 --- a/apps/ccam/statehandlerutils.h +++ b/apps/ccam/statehandlerutils.h @@ -1,274 +1,277 @@ #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/app/Application.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::app; using namespace rosa::terminal; // For the convinience to write a shorter data type name using SignalStateTuple = AppTuple; 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>( static_cast &>(I.first)) << "\n******\n"; auto StateInfo = SigSD->detectSignalState( std::get<0>(static_cast &>(I.first))); if (I.second) { SignalStateTuple Res = { std::get<0>(static_cast &>(I.first)), StateInfo.StateID, StateInfo.SignalProperty, StateInfo.ConfidenceOfMatchingState, StateInfo.ConfidenceOfMismatchingState, StateInfo.ConfidenceStateIsValid, StateInfo.ConfidenceStateIsInvalid, StateInfo.ConfidenceStateIsStable, StateInfo.ConfidenceStateIsDrifting, StateInfo.StateCondition, StateInfo.NumberOfInsertedSamplesAfterEntrance, static_cast( (StateInfo.StateIsValid ? 4 : 0) + (StateInfo.StateJustGotValid ? 2 : 0) + (StateInfo.StateIsValidAfterReentrance ? 1 : 0))}; return Result(Res); } return Result(); })); } // System State using SystemStateTuple = AppTuple; 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: Change it from global to local variable if possible std::shared_ptr< SystemStateDetector> SysSD; template struct function { ret operator()(A a) { std::vector> SignalStateInfos; std::stringstream OutString; for (auto _SignalStateTuple : a) { // convert tuple to info struct out.push_back({}); //OutString << std::get<0>(_SignalStateTuple.first) << ","; SignalStateInformation Info; Info.StateID = std::get<1>(_SignalStateTuple.first); Info.SignalProperty = static_cast(std::get<2>(_SignalStateTuple.first)); Info.ConfidenceOfMatchingState = std::get<3>(_SignalStateTuple.first); Info.ConfidenceOfMismatchingState = std::get<4>(_SignalStateTuple.first); Info.ConfidenceStateIsValid = std::get<5>(_SignalStateTuple.first); Info.ConfidenceStateIsInvalid = std::get<6>(_SignalStateTuple.first); Info.ConfidenceStateIsStable = std::get<7>(_SignalStateTuple.first); Info.ConfidenceStateIsDrifting = std::get<8>(_SignalStateTuple.first); Info.StateCondition = static_cast(std::get<9>(_SignalStateTuple.first)); Info.NumberOfInsertedSamplesAfterEntrance = std::get<10>(_SignalStateTuple.first); Info.StateIsValid = (std::get<11>(_SignalStateTuple.first) & 4) > 0; Info.StateJustGotValid = (std::get<11>(_SignalStateTuple.first) & 2) > 0; Info.StateIsValidAfterReentrance = (std::get<11>(_SignalStateTuple.first) & 1) > 0; SignalStateInfos.push_back(Info); } SystemStateInformation SystemStateInfo = SysSD->detectSystemState(SignalStateInfos); //OutString << SystemStateInfo.StateID << ","; //OutString << SystemStateInfo.ConfidenceStateIsValid << ","; //OutString << SystemStateInfo.ConfidenceStateIsInvalid << ","; //OutString << SystemStateInfo.ConfidenceOfInputsMatchingState << ","; //OutString << SystemStateInfo.ConfidenceOfInputsMismatchingState << ","; //OutString << SystemStateInfo.ConfidenceOfOutputsMatchingState << ","; //OutString << SystemStateInfo.ConfidenceOfOutputsMismatchingState << ","; OutString << SystemStateInfo.StateCondition << ","; //OutString << SystemStateInfo.ConfidenceSystemIsFunctioning << ","; //OutString << SystemStateInfo.ConfidenceSystemIsMalfunctioning << ","; - //OutString << SystemStateInfo.ConfidenceOfAllDecisions << ","; + OutString << SystemStateInfo.ConfidenceStateIsDrifting << ","; + OutString << SystemStateInfo.ConfidenceStateIsDriftingUp << ","; + OutString << SystemStateInfo.ConfidenceStateIsDriftingDown << ","; + OutString << SystemStateInfo.ConfidenceOfAllDecisions << ","; return ret(std::make_tuple(OutString.str())); } }; 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; std::shared_ptr< SystemStateDetector> _SysSD( new SystemStateDetector( std::numeric_limits::max(), NumOfSlaves, BrokenDelayFunction, OkDelayFunction)); SysSD = _SysSD; auto HandlerFunction = Handler, arr>, Input>::function; 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 } } // template // AgentHandle createMQTTSensor(std::string MQTTTopic) { // using Input = void; // 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>( // static_cast &>(I.first)) // << "\n******\n"; // auto StateInfo = SigSD->detectSignalState( // std::get<0>(static_cast &>(I.first))); // if (I.second) { // SignalStateTuple Res = { // std::get<0>(static_cast &>(I.first)), // StateInfo.StateID, // StateInfo.SignalProperty, // StateInfo.ConfidenceOfMatchingState, // StateInfo.ConfidenceOfMismatchingState, // StateInfo.ConfidenceStateIsValid, // StateInfo.ConfidenceStateIsInvalid, // StateInfo.ConfidenceStateIsStable, // StateInfo.ConfidenceStateIsDrifting, // StateInfo.StateCondition, // StateInfo.NumberOfInsertedSamplesAfterEntrance, // static_cast( // (StateInfo.StateIsValid ? 4 : 0) + // (StateInfo.StateJustGotValid ? 2 : 0) + // (StateInfo.StateIsValidAfterReentrance ? 1 : 0))}; // return Result(Res); // } // return Result(); // })); // } #endif // STATEHANDLERUTILS_H diff --git a/include/rosa/agent/SystemState.hpp b/include/rosa/agent/SystemState.hpp index a9f0565..d9e3ce5 100644 --- a/include/rosa/agent/SystemState.hpp +++ b/include/rosa/agent/SystemState.hpp @@ -1,304 +1,312 @@ //===-- rosa/agent/SystemState.hpp ------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/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/agent/State.hpp" #include "rosa/support/debug.hpp" #include namespace rosa { namespace agent { enum class SystemStateRelation : uint8_t { STATEISMATCHING = 0, ///< The system state is matching ONLYINPUTISMATCHING = 1, ///< Only inputs of the system state are matching ONLYOUTPUTISMATCHING = 2, ///< Only outputs of the system state are matching STATEISMISMATCHING = 3 ///< The system state is mismatching }; /// TODO: write description template struct SystemStateInformation : StateInformation { /// TODO: describe CONFDATATYPE ConfidenceOfInputsMatchingState; CONFDATATYPE ConfidenceOfInputsMismatchingState; CONFDATATYPE ConfidenceOfOutputsMatchingState; CONFDATATYPE ConfidenceOfOutputsMismatchingState; CONFDATATYPE ConfidenceSystemIsFunctioning; CONFDATATYPE ConfidenceSystemIsMalfunctioning; CONFDATATYPE ConfidenceOfAllDecisions; public: SystemStateInformation() = default; SystemStateInformation(unsigned int _SystemStateID, StateConditions _StateCondition) { this->StateID = _SystemStateID; this->StateCondition = _StateCondition; this->StateIsValid = false; this->StateJustGotValid = false; this->StateIsValidAfterReentrance = false; this->ConfidenceOfInputsMatchingState = 0; this->ConfidenceOfInputsMismatchingState = 0; this->ConfidenceOfOutputsMatchingState = 0; this->ConfidenceOfOutputsMismatchingState = 0; this->ConfidenceSystemIsFunctioning = 0; this->ConfidenceSystemIsMalfunctioning = 0; this->ConfidenceOfAllDecisions = 0; } }; // todo: do we need PROCDATATYPE? /// TODO TEXT template class SystemState : public State { // 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: /// SignalStateInfo is a struct of SignalStateInformation that contains /// information about the current signal state. SystemStateInformation SystemStateInfo; std::vector> SignalStateInfos; uint32_t NumberOfSignals; public: /// TODO: write description SystemState(uint32_t StateID, uint32_t NumberOfSignals) noexcept : SystemStateInfo(StateID, StateConditions::UNKNOWN), NumberOfSignals(NumberOfSignals) { SignalStateInfos.resize(NumberOfSignals); } /// Destroys \p this object. ~SystemState(void) = default; /// TODO: write description SystemStateInformation insertSignalStateInformation( const std::vector> _SignalStateInfos) noexcept { ASSERT(_SignalStateInfos.size() == NumberOfSignals); bool AllSignalsAreValid = true; bool AtLeastOneSignalJustGotValid = false; bool AllSignalsAreValidAfterReentrance = true; bool AtLeastOneSignalIsUnknown = false; bool AllSignalsAreStable = true; uint32_t DriftDnCounter = 0; uint32_t DriftUpCounter = 0; // TODO: change this SystemStateInfo.ConfidenceOfInputsMatchingState = 1; SystemStateInfo.ConfidenceOfInputsMismatchingState = 0; SystemStateInfo.ConfidenceOfOutputsMatchingState = 1; SystemStateInfo.ConfidenceOfOutputsMismatchingState = 0; SystemStateInfo.ConfidenceStateIsValid = 1; SystemStateInfo.ConfidenceStateIsInvalid = 0; SystemStateInfo.ConfidenceStateIsStable = 1; SystemStateInfo.ConfidenceStateIsDrifting = 0; + SystemStateInfo.ConfidenceStateIsDriftingUp = 0; + SystemStateInfo.ConfidenceStateIsDriftingDown = 0; std::size_t counter = 0; for (auto SSI : _SignalStateInfos) { if (!SSI.StateIsValid) AllSignalsAreValid = false; if (SSI.StateJustGotValid) AtLeastOneSignalJustGotValid = true; if (!SSI.StateIsValidAfterReentrance) AllSignalsAreValidAfterReentrance = false; if (SSI.StateCondition == StateConditions::UNKNOWN) AtLeastOneSignalIsUnknown = true; if (SSI.StateCondition == StateConditions::DRIFTING_DN) { AllSignalsAreStable = false; DriftDnCounter++; } if (SSI.StateCondition == StateConditions::DRIFTING_UP) { AllSignalsAreStable = false; DriftUpCounter++; } if (SSI.SignalProperty == SignalProperties::INPUT) { SystemStateInfo.ConfidenceOfInputsMatchingState = fuzzyAND(SystemStateInfo.ConfidenceOfInputsMatchingState, SSI.ConfidenceOfMatchingState); SystemStateInfo.ConfidenceOfInputsMismatchingState = fuzzyOR(SystemStateInfo.ConfidenceOfInputsMismatchingState, SSI.ConfidenceOfMismatchingState); } else { SystemStateInfo.ConfidenceOfOutputsMatchingState = fuzzyAND(SystemStateInfo.ConfidenceOfOutputsMatchingState, SSI.ConfidenceOfMatchingState); SystemStateInfo.ConfidenceOfOutputsMismatchingState = fuzzyOR(SystemStateInfo.ConfidenceOfOutputsMismatchingState, SSI.ConfidenceOfMismatchingState); } SystemStateInfo.ConfidenceStateIsValid = fuzzyAND( SystemStateInfo.ConfidenceStateIsValid, SSI.ConfidenceStateIsValid); SystemStateInfo.ConfidenceStateIsInvalid = fuzzyOR(SystemStateInfo.ConfidenceStateIsInvalid, SSI.ConfidenceStateIsInvalid); SystemStateInfo.ConfidenceStateIsStable = fuzzyAND( SystemStateInfo.ConfidenceStateIsStable, SSI.ConfidenceStateIsStable); SystemStateInfo.ConfidenceStateIsDrifting = fuzzyOR(SystemStateInfo.ConfidenceStateIsDrifting, SSI.ConfidenceStateIsDrifting); + SystemStateInfo.ConfidenceStateIsDriftingUp = + fuzzyOR(SystemStateInfo.ConfidenceStateIsDriftingUp, + SSI.ConfidenceStateIsDriftingUp); + SystemStateInfo.ConfidenceStateIsDriftingDown = + fuzzyOR(SystemStateInfo.ConfidenceStateIsDriftingDown, + SSI.ConfidenceStateIsDriftingDown); this->SignalStateInfos.at(counter) = SSI; counter++; } SystemStateInfo.StateIsValid = AllSignalsAreValid; SystemStateInfo.StateJustGotValid = AllSignalsAreValid && AtLeastOneSignalJustGotValid; SystemStateInfo.StateIsValidAfterReentrance = AllSignalsAreValidAfterReentrance; if (AtLeastOneSignalIsUnknown) SystemStateInfo.StateCondition = StateConditions::UNKNOWN; else if (AllSignalsAreStable) SystemStateInfo.StateCondition = StateConditions::STABLE; else { if (DriftDnCounter > DriftUpCounter) SystemStateInfo.StateCondition = StateConditions::DRIFTING_DN; else if (DriftDnCounter < DriftUpCounter) SystemStateInfo.StateCondition = StateConditions::DRIFTING_UP; else { SystemStateInfo.StateCondition = StateConditions::DRIFTING; } } return SystemStateInfo; } /// TODO: write description // TODO (future): think about saving the state information in a history SystemStateRelation compareSignalStateInformation( const std::vector> _SignalStateInfos) noexcept { bool inputsAreMatching = true; bool outputsAreMatching = true; std::size_t counter = 0; for (auto SSI : _SignalStateInfos) { if (this->SignalStateInfos.at(counter).StateID != SSI.StateID) { if (SSI.SignalProperty == SignalProperties::INPUT) inputsAreMatching = false; else // SignalProperties::OUTPUT outputsAreMatching = false; } counter++; } if (inputsAreMatching && outputsAreMatching) return SystemStateRelation::STATEISMATCHING; else if (inputsAreMatching && !outputsAreMatching) return SystemStateRelation::ONLYINPUTISMATCHING; else if (!inputsAreMatching && outputsAreMatching) return SystemStateRelation::ONLYOUTPUTISMATCHING; else return SystemStateRelation::STATEISMISMATCHING; } #ifdef ADDITIONAL_FUNCTIONS /// TODO: write description template void insertSignalStateInformation( const std::array, size> &Data) noexcept { ASSERT(size <= NumberOfSignals); std::size_t counter = 0; for (auto tmp : Data) { Signals.at(counter) = tmp; counter++; } } /// TODO: write description template std::enable_if_t>...>, void> insertSignalStateInformation(Types... Data) { // TODO (future): think about saving the state information in a history insertSignalStateInfos( std::array, sizeof...(Data)>( {Data...})); } // returns true if they are identical /// TODO: write description template bool compareSignalStateInformation( const std::array, size> &Data) noexcept { // TODO (future): think about saving the state information in a history std::size_t counter = 0; for (auto tmp : Data) { if (Signals.at(counter) != tmp) return false; counter++; } return true; } // checks only the given amount /// TODO: write description template std::enable_if_t>...>, bool> compareSignalStateInformation(Types... Data) { return compareSignalStateInfos( std::array, sizeof...(Data)>( {Data...})); } #endif /// Gives information about the current signal state. /// /// \return a struct SignalStateInformation that contains information about /// the current signal state. SystemStateInformation systemStateInformation(void) noexcept { return SystemStateInfo; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_SYSTEMSTATE_HPP