ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
UserInputValidator.cc
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
4
5#include "Module.h"
6
7#include <utility>
8
9namespace ChimeraTK {
10
11 /********************************************************************************************************************/
12
14 const std::function<bool(void)>& isValidFunction, const std::string& errorMessage) {
15 // create validator and store in list
16 _validators.emplace_back(isValidFunction, errorMessage);
17
18 return &_validators.back();
19 }
20
21 /********************************************************************************************************************/
22
23 void UserInputValidator::setErrorFunction(const std::function<void(const std::string&)>& errorFunction) {
24 _errorFunction = errorFunction;
25 }
26
27 /********************************************************************************************************************/
28
29 bool UserInputValidator::validate(const ChimeraTK::TransferElementID& change) {
30 if(!change.isValid()) {
31 return validateAll();
32 }
33
34 if(!_finalised) {
35 throw ChimeraTK::logic_error("Initial values were not validated");
36 }
37
38 // We have downstream channels that signalized a change - invalidate all of our
39 if(_downstreamInvalidatingReturnChannels.count(change) > 0) {
41 for(auto& v : _variableMap) {
43 }
44
45 return false;
46 }
47
48 if(!_validatorMap.count(change)) {
49 return false;
50 }
51
52 for(auto* validator : _validatorMap.at(change)) {
53 if(!validator->isValidFunction()) {
54 _errorFunction(validator->errorMessage);
56 return true;
57 }
58 }
59
60 _variableMap.at(change)->accept();
61 return false;
62 }
63
64 /********************************************************************************************************************/
65
67 if(!_finalised) {
68 finalise();
69 }
70 bool rejected = false;
71 for(auto& v : _variableMap) {
72 rejected |= validate(v.first);
73 }
74 return rejected;
75 }
76
77 /********************************************************************************************************************/
78
80 if(_finalised) {
81 return;
82 }
83
84 if(_module == nullptr) {
85 throw ChimeraTK::logic_error("UserInputValidator was finalised without any call to add()");
86 }
87
88 for(auto& accessor : _module->getAccessorListRecursive()) {
89 if(accessor.getDirection() == VariableDirection{VariableDirection::feeding, true} &&
90 accessor.getModel().getTags().count(std::string(UserInputValidator::tagValidatedVariable)) > 0) {
91 _downstreamInvalidatingReturnChannels.emplace(accessor.getAppAccessorNoType().getId());
92 }
93 }
94
95 // Find longest path that is validated in our model, starting at this module
96
97 // Step 1: Do a topological order of the tree of modules with a return channel, starting from this module
98 std::deque<Model::ApplicationModuleProxy> stack;
99 std::map<ApplicationModule*, int> distances;
100
101 // Visitor that a) builds the order in stack and b) initialises the distance array used later to calculate
102 // the distance from this node to the entry in that array
103 auto orderVisitor = [&](auto proxy) {
104 if constexpr(Model::isApplicationModule(proxy)) {
105 stack.push_front(proxy);
106 distances.try_emplace(&proxy.getApplicationModule(), std::numeric_limits<int>::min());
107 }
108 };
109
110 // March through the tree with post order to properly build the stack. Technically sort a tree that is larger
111 // that what we want to look at, because we cannot stop the visit if there is a PV access with return channel
112 // that does not correspond to another UserInputValidator. However these distances should then left
113 // uninitialised in the distance calculation below and not taken into account
114 _module->getModel().visit(orderVisitor, Model::visitOrderPost, Model::depthFirstSearch,
115 Model::keepPvAccesWithReturnChannel, Model::keepApplicationModules);
116
117 // Step 2: From the topological sort of the subtree, calculate the distances from our module to the currently
118 // checked module.
119 distances[_module] = 0;
120 std::unordered_set<ApplicationModule*> downstreamModulesWithFeedback;
121
122 auto downstreamModuleCollector = [&](auto proxy) {
123 if constexpr(Model::isApplicationModule(proxy)) {
124 downstreamModulesWithFeedback.insert(&proxy.getApplicationModule());
125 }
126 };
127
128 auto connectingVariableVisitor = [&](auto proxy) {
129 if constexpr(Model::isVariable(proxy)) {
130 proxy.visit(downstreamModuleCollector, Model::adjacentOutSearch, Model::keepPvAccesWithReturnChannel,
131 Model::keepProcessVariables);
132 }
133 };
134
135 for(const auto& stackEntry : stack) {
136 downstreamModulesWithFeedback.clear();
137
138 // We need to find all connected modules to the module currently looked at. Unfortunately we need to
139 // do that with a double visit, because the connection via PV access edges is not directly between modules
140 // but through a variable Vertex. So we first jump to the Variable using adjacentOut and in that visitor
141 // collect the modules into the downstreamModulesWithFeedback set
142 stackEntry.visit(connectingVariableVisitor, Model::adjacentOutSearch, Model::keepApplicationModules,
143 Model::keepPvAccesWithReturnChannel);
144
145 // The distances from this module to the module on the stack is then just updated to be either what it
146 // was or the distance from the currently looked-at stack entry + 1 (since we have equal weights for the
147 // edges)
148 for(auto* vtx : downstreamModulesWithFeedback) {
149 distances[vtx] = std::max(distances[vtx], distances[&stackEntry.getApplicationModule()] + 1);
150 }
151 }
152
153 _validationDepth = 1 + std::max_element(distances.begin(), distances.end(), [](auto& a, auto& b) -> bool {
154 return a.second < b.second;
155 })->second;
156
157 for(auto& v : _variableMap) {
158 v.second->setHistorySize(_validationDepth);
159 }
160
161 _finalised = true;
162 }
163
164 /********************************************************************************************************************/
165
166 UserInputValidator::Validator::Validator(std::function<bool(void)> isValidTest, std::string initialErrorMessage)
167 : isValidFunction(std::move(isValidTest)), errorMessage(std::move(initialErrorMessage)) {}
168
169 /********************************************************************************************************************/
170
171} // namespace ChimeraTK
void setCurrentVersionNumber(VersionNumber versionNumber) override
Set the current version number.
ChimeraTK::Model::ApplicationModuleProxy getModel()
Return the application model proxy representing this module.
std::list< VariableNetworkNode > getAccessorListRecursive() const
Obtain the list of accessors/variables associated with this instance and any submodules.
auto visit(VISITOR visitor, Args... args) const
Traverse the model using the specified filter and call the visitor functor for each ModuleGroup,...
Definition Model.h:1451
constexpr bool isApplicationModule(const PROPERTY_OR_PROXY &)
Definition Model.h:637
constexpr bool isVariable(const PROPERTY_OR_PROXY &)
Definition Model.h:661
InvalidityTracer application module.
Validator * addValidator(const std::function< bool(void)> &isValidFunction, const std::string &errorMessage)
bool validate(const ChimeraTK::TransferElementID &change)
Execute all validations for the given change.
void setErrorFunction(const std::function< void(const std::string &)> &errorFunction)
Define how to report error messages to the user.
static constexpr std::string_view tagValidatedVariable
std::list< Validator > _validators
std::unordered_set< ChimeraTK::TransferElementID > _downstreamInvalidatingReturnChannels
std::map< ChimeraTK::TransferElementID, std::shared_ptr< VariableBase > > _variableMap
std::map< ChimeraTK::TransferElementID, std::vector< Validator * > > _validatorMap
std::function< void(const std::string &)> _errorFunction
bool validateAll()
Evaluate all validation conditions and correct all invalid values.
Struct to define the direction of variables.
Definition Flags.h:13