diff --git a/include/rosa/agent/SignalStateDetector.hpp b/include/rosa/agent/SignalStateDetector.hpp index 103c0ec..cca00f0 100644 --- a/include/rosa/agent/SignalStateDetector.hpp +++ b/include/rosa/agent/SignalStateDetector.hpp @@ -1,311 +1,370 @@ //===-- rosa/agent/SignalStateDetector.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/SignalStateDetector.hpp /// /// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *signal state detector* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_SIGNALSTATEDETECTOR_HPP #define ROSA_AGENT_SIGNALSTATEDETECTOR_HPP #include "rosa/agent/Functionality.h" #include "rosa/agent/SignalState.hpp" #include "rosa/agent/StateDetector.hpp" #include namespace rosa { namespace agent { /// Implements \c rosa::agent::SignalStateDetector as a functionality that /// detects signal states given on input samples. /// /// \note This implementation is supposed to be used for samples of an /// arithmetic type. /// /// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of /// data in that the confidence values are given, \tparam PROCDATATYPE type of /// the relative distance and the type of data in which DABs are saved. template class SignalStateDetector : public StateDetector { using StateDetector = StateDetector; using PartFuncPointer = typename StateDetector::PartFuncPointer; using StepFuncPointer = typename StateDetector::StepFuncPointer; private: // For the convinience to write a shorter data type name using SignalStatePtr = std::shared_ptr>; /// The SignalProperty saves whether the monitored signal is an input our /// output signal. SignalProperties SignalProperty; /// The CurrentSignalState is a pointer to the (saved) signal state in which /// the actual variable (signal) of the observed system is. SignalStatePtr CurrentSignalState; /// The DetectedSignalStates is a history in that all detected signal states /// are saved. DynamicLengthHistory DetectedSignalStates; /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// confidence how good the new sample matches another sample in the sample /// history. This is done to evaluate whether one sample belongs to an /// existing state. PartFuncPointer FuzzyFunctionSampleMatches; /// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the /// confidence how bad the new sample matches another sample in the sample /// history. This is done to evaluate whether one sample does not belong to an /// existing state. PartFuncPointer FuzzyFunctionSampleMismatches; /// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the /// confidence how many samples from the sample history match the new sample. /// This is done to evaluate whether one sample belongs to an existing state. StepFuncPointer FuzzyFunctionNumOfSamplesMatches; /// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives /// the confidence how many samples from the sample history mismatch the new /// sample. This is done to evaluate whether one sample does not belong to an /// existing state. StepFuncPointer FuzzyFunctionNumOfSamplesMismatches; /// The FuzzyFunctionSampleValid is the fuzzy function that gives the /// confidence how good one matches another sample in the sample /// history. This is done to evaluate whether a state is valid. PartFuncPointer FuzzyFunctionSampleValid; /// The FuzzyFunctionSampleInvalid is the fuzzy function that gives the /// confidence how bad one sample matches another sample in the sample /// history. This is done to evaluate whether a state is invalid. PartFuncPointer FuzzyFunctionSampleInvalid; /// The FuzzyFunctionNumOfSamplesValid is the fuzzy function that gives the /// confidence how many samples from the sample history match another sample. /// This is done to evaluate whether a state is valid. StepFuncPointer FuzzyFunctionNumOfSamplesValid; /// The FuzzyFunctionNumOfSamplesInvalid is the fuzzy function that gives /// the confidence how many samples from the sample history mismatch another /// sample. This is done to evaluate whether a state is invalid. StepFuncPointer FuzzyFunctionNumOfSamplesInvalid; /// The FuzzyFunctionSignalIsDrifting is the fuzzy function that gives the /// confidence how likely it is that the signal is drifting. PartFuncPointer FuzzyFunctionSignalIsDrifting; /// The FuzzyFunctionSignalIsStable is the fuzzy function that gives the /// confidence how likely it is that the signal is stable (not drifting). PartFuncPointer FuzzyFunctionSignalIsStable; /// SampleHistorySize is the (maximum) size of the sample history. uint32_t SampleHistorySize; /// DABSize the size of a DAB (Discrete Average Block). uint32_t DABSize; /// DABHistorySize is the (maximum) size of the DAB history. uint32_t DABHistorySize; public: /// Creates an instance by setting all parameters /// \param FuzzyFunctionSampleMatches The FuzzyFunctionSampleMatches is the /// fuzzy function that gives the confidence how good the new sample matches /// another sample in the sample history. /// /// \param FuzzyFunctionSampleMismatches The FuzzyFunctionSampleMismatches is /// the fuzzy function that gives the confidence how bad the new sample /// matches another sample in the sample history. /// /// \param FuzzyFunctionNumOfSamplesMatches The /// FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the /// confidence how many samples from the sampe history match the new sample. /// /// \param FuzzyFunctionNumOfSamplesMismatches The /// FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives the /// confidence how many samples from the sampe history mismatch the new /// sample. /// /// \param FuzzyFunctionSignalIsDrifting The FuzzyFunctionSignalIsDrifting is /// the fuzzy function that gives the confidence how likely it is that the /// signal (resp. the state of a signal) is drifting. /// /// \param FuzzyFunctionSignalIsStable The FuzzyFunctionSignalIsStable is the /// fuzzy function that gives the confidence how likely it is that the signal /// (resp. the state of a signal) is stable (not drifting). /// /// \param SampleHistorySize Sets the History size which will be used by \c /// SignalState. /// /// \param DABSize Sets the DAB size which will be used by \c SignalState. /// /// \param DABHistorySize Sets the size which will be used by \c SignalState. /// SignalStateDetector(SignalProperties SignalProperty, uint32_t MaximumNumberOfSignalStates, PartFuncPointer FuzzyFunctionSampleMatches, PartFuncPointer FuzzyFunctionSampleMismatches, StepFuncPointer FuzzyFunctionNumOfSamplesMatches, StepFuncPointer FuzzyFunctionNumOfSamplesMismatches, PartFuncPointer FuzzyFunctionSampleValid, PartFuncPointer FuzzyFunctionSampleInvalid, StepFuncPointer FuzzyFunctionNumOfSamplesValid, StepFuncPointer FuzzyFunctionNumOfSamplesInvalid, PartFuncPointer FuzzyFunctionSignalIsDrifting, PartFuncPointer FuzzyFunctionSignalIsStable, uint32_t SampleHistorySize, uint32_t DABSize, uint32_t DABHistorySize) noexcept : SignalProperty(SignalProperty), CurrentSignalState(nullptr), DetectedSignalStates(MaximumNumberOfSignalStates), FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches), FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches), FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches), FuzzyFunctionNumOfSamplesMismatches( FuzzyFunctionNumOfSamplesMismatches), FuzzyFunctionSampleValid(FuzzyFunctionSampleValid), FuzzyFunctionSampleInvalid(FuzzyFunctionSampleInvalid), FuzzyFunctionNumOfSamplesValid(FuzzyFunctionNumOfSamplesValid), FuzzyFunctionNumOfSamplesInvalid(FuzzyFunctionNumOfSamplesInvalid), FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting), FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable), SampleHistorySize(SampleHistorySize), DABSize(DABSize), DABHistorySize(DABHistorySize) { this->NextStateID = 1; this->StateHasChanged = false; } /// Destroys \p this object. ~SignalStateDetector(void) = default; /// Detects the signal state to which the new sample belongs or create a new /// signal state if the new sample does not match to any of the saved states. /// /// \param Sample is the actual sample of the observed signal. /// /// \return the information of the current signal state (signal state ID and /// other parameters). // TODO (future): change this function to an operator()-function SignalStateInformation detectSignalState(INDATATYPE Sample) noexcept { if (!CurrentSignalState) { ASSERT(DetectedSignalStates.empty()); SignalStatePtr S = createNewSignalState(); CurrentSignalState = S; } else { - // TODO (future): maybe there is a better way than a relative distance - // comparison. Maybe somehow a mix of relative and absolute? - CONFDATATYPE ConfidenceSampleMatchesSignalState = - CurrentSignalState->confidenceSampleMatchesSignalState(Sample); - CONFDATATYPE ConfidenceSampleMismatchesSignalState = + + std::vector ConfidenceSampleMatchesSignalStateBestValueVector = {}; + std::vector ConfidenceSampleMatchesSignalStateBestStateVector = {}; + std::vector ConfidenceSampleMatchesSignalStateBestStateIDVector = {}; + + SignalStatePtr newSignaleState = nullptr; + + CONFDATATYPE ConfidenceSampleMatchesSignalState = + CurrentSignalState->confidenceSampleMatchesSignalState(Sample); + CONFDATATYPE ConfidenceSampleMismatchesSignalState = CurrentSignalState->confidenceSampleMismatchesSignalState(Sample); - this->StateHasChanged = ConfidenceSampleMatchesSignalState <= - ConfidenceSampleMismatchesSignalState; + // check if current state is still ok + if (ConfidenceSampleMatchesSignalState > + ConfidenceSampleMismatchesSignalState) { + + // current state is still fine, so save it but later compare with all other + // for now, no state change (maybe later we find other valid states) + ConfidenceSampleMatchesSignalStateBestValueVector.push_back(ConfidenceSampleMatchesSignalState); + ConfidenceSampleMatchesSignalStateBestStateVector.push_back(CurrentSignalState); + ConfidenceSampleMatchesSignalStateBestStateIDVector.push_back(CurrentSignalState->signalStateInformation().StateID); + this->StateHasChanged = false; + //std::cout << "Current state " << CurrentSignalState->signalStateInformation().StateID << " is still valid \n"; + } + else{ + //State has to be change as current state is not valid anymore + //std::cout << "Current state " << CurrentSignalState->signalStateInformation().StateID << " has to be changed \n"; + this->StateHasChanged = true; + + } + + // check all other states (no matter if current is still valid) + for (auto &SavedSignalState : DetectedSignalStates) { + ConfidenceSampleMatchesSignalState = + SavedSignalState->confidenceSampleMatchesSignalState(Sample); + ConfidenceSampleMismatchesSignalState = + SavedSignalState->confidenceSampleMismatchesSignalState(Sample); + + // only take states into account where matching>missmatching + // if one valid sate is found, save it to stae vactor for possible states + if (ConfidenceSampleMatchesSignalState > + ConfidenceSampleMismatchesSignalState) { + + ConfidenceSampleMatchesSignalStateBestValueVector.push_back(ConfidenceSampleMatchesSignalState); + ConfidenceSampleMatchesSignalStateBestStateVector.push_back(SavedSignalState); + ConfidenceSampleMatchesSignalStateBestStateIDVector.push_back(SavedSignalState->signalStateInformation().StateID); + //this->StateHasChanged = true; Not necessary?! + //std::cout << "Found other valid match for State ID" << SavedSignalState->signalStateInformation().StateID <<"\n"; + } + } + + //std::cout << "Found " << ConfidenceSampleMatchesSignalStateBestValueVector.size() <<" valid states for this sample\n"; + + // now check how many valid states have been found + + // if just one -> that means old state is not valid anymore + // set new state, StateHasChanged is already true + if(ConfidenceSampleMatchesSignalStateBestValueVector.size()==1){ + newSignaleState = ConfidenceSampleMatchesSignalStateBestStateVector[0]; + //std::cout <<"Only this state in Vector: "<< ConfidenceSampleMatchesSignalStateBestStateVector[0]->signalStateInformation().StateID <<"\n"; + //std::cout <<"Old state was: "<< CurrentSignalState->signalStateInformation().StateID <<"\n"; + } + // if there is more than 1 valid state -> get minimum state ID and change current state to minimum + // if this results in statechange, set StateHasChanged to True + else if (ConfidenceSampleMatchesSignalStateBestValueVector.size()>=2){ + + unsigned int minElementIndex = std::min_element(ConfidenceSampleMatchesSignalStateBestStateIDVector.begin(),ConfidenceSampleMatchesSignalStateBestStateIDVector.end()) - ConfidenceSampleMatchesSignalStateBestStateIDVector.begin(); + unsigned int minElement = *std::min_element(ConfidenceSampleMatchesSignalStateBestStateIDVector.begin(), ConfidenceSampleMatchesSignalStateBestStateIDVector.end()); + + //std::cout << "Sample matches multiple Signals: " << minElement << " was chosen as minimum" <<"\n"; + + newSignaleState = ConfidenceSampleMatchesSignalStateBestStateVector[minElementIndex]; + + if(minElement == CurrentSignalState->signalStateInformation().StateID) + this->StateHasChanged = false; + else + this->StateHasChanged = true; + } + else + newSignaleState = nullptr; + + // if state has changed, leave old one if (this->StateHasChanged) { - if (CurrentSignalState->signalStateInformation().StateIsValid) - CurrentSignalState->leaveSignalState(); + if (CurrentSignalState->signalStateInformation().StateIsValid){ + CurrentSignalState->leaveSignalState(); + } else DetectedSignalStates.deleteEntry(CurrentSignalState); - // TODO (future): additionally save averages to enable fast iteration - // through recorded signl state history (maybe sort vector based on - // these average values) - CurrentSignalState = nullptr; - - for (auto &SavedSignalState : DetectedSignalStates) { - ConfidenceSampleMatchesSignalState = - SavedSignalState->confidenceSampleMatchesSignalState(Sample); - ConfidenceSampleMismatchesSignalState = - SavedSignalState->confidenceSampleMismatchesSignalState(Sample); - - if (ConfidenceSampleMatchesSignalState > - ConfidenceSampleMismatchesSignalState) { - // TODO (future): maybe it would be better to compare - // ConfidenceSampleMatchesSignalState of all signal states in the - // vector in order to find the best matching signal state. - CurrentSignalState = SavedSignalState; - break; - } - } + // set new state + CurrentSignalState = newSignaleState; + // if there is no existing one, create new one if (!CurrentSignalState) { SignalStatePtr S = createNewSignalState(); CurrentSignalState = S; } } } + // insert current sample to new current state SignalStateInformation SignalStateInfo = CurrentSignalState->insertSample(Sample); + //increase nextstateid if a new state got active if (SignalStateInfo.StateJustGotValid) { this->NextStateID++; } return SignalStateInfo; } /// Gives information about the current signal state. /// /// \return a struct SignalStateInformation that contains information about /// the current signal state or NULL if no current signal state exists. SignalStateInformation currentSignalStateInformation(void) noexcept { if (CurrentSignalState) { return CurrentSignalState->signalStateInformation(); } else { return NULL; } } /// Gives information whether a signal state change has happened or not. /// /// \return true if a signal state change has happened, and false if not. bool stateHasChanged(void) noexcept { return this->StateHasChanged; } private: /// Creates a new signal state and adds it to the signal state vector in which /// all known states are saved. /// /// \return a pointer to the newly created signal state or NULL if no state /// could be created. SignalStatePtr createNewSignalState(void) noexcept { SignalStatePtr S(new SignalState( this->NextStateID, SignalProperty, SampleHistorySize, DABSize, DABHistorySize, *FuzzyFunctionSampleMatches, *FuzzyFunctionSampleMismatches, *FuzzyFunctionNumOfSamplesMatches, *FuzzyFunctionNumOfSamplesMismatches, *FuzzyFunctionSampleValid, *FuzzyFunctionSampleInvalid, *FuzzyFunctionNumOfSamplesValid, *FuzzyFunctionNumOfSamplesInvalid, *FuzzyFunctionSignalIsDrifting, *FuzzyFunctionSignalIsStable)); DetectedSignalStates.addEntry(S); return S; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_SIGNALSTATEDETECTOR_HPP