ChimeraTK-ApplicationCore 04.07.01
Loading...
Searching...
No Matches
InversionOfControlAccessor.h
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
3#pragma once
4
5#include "Application.h"
6#include "ApplicationModule.h"
7#include "Module.h"
8#include "Utilities.h"
10
11#include <boost/smart_ptr/shared_ptr.hpp>
12
13#include <string>
14
15namespace ChimeraTK {
16
17 /********************************************************************************************************************/
18
22 template<typename Derived>
24 public:
27
33 void setMetaData(const std::optional<std::string>& name, const std::optional<std::string>& unit = {},
34 const std::optional<std::string>& description = {},
35 const std::optional<std::unordered_set<std::string>>& tags = {});
36
39 void addTag(const std::string& tag) { _node.addTag(tag); }
40
43 void addTags(const std::unordered_set<std::string>& tags);
44
46 const std::unordered_set<std::string>& getTags();
47
49 explicit operator VariableNetworkNode() { return _node; }
50 explicit operator VariableNetworkNode() const { return _node; }
51
53 void replace(Derived&& other);
54
56 [[nodiscard]] EntityOwner* getOwner() const { return _node.getOwningModule(); }
57
58 [[nodiscard]] Model::ProcessVariableProxy getModel() const { return _node.getModel(); }
59
61 template<typename UserType>
62 [[nodiscard]] bool checkMetadataWriteDifference();
63
64 protected:
66 InversionOfControlAccessor(Module* owner, const std::string& name, VariableDirection direction, std::string unit,
67 size_t nElements, UpdateMode mode, const std::string& description, const std::type_info* valueType,
68 const std::unordered_set<std::string>& tags = {});
69
72
74 [[nodiscard]] std::string completeDescription(EntityOwner* owner, const std::string& description) const;
75
78
83 void init();
84
89 void deinit();
90
92
93 private:
94 bool _initCalled{false};
95 bool _deinitCalled{false};
96 };
97
98 /********************************************************************************************************************/
99 /********************************************************************************************************************/
100
101 template<typename Derived>
103 if(_node.getType() == NodeType::Application) {
104 assert(_initCalled);
105 assert(_deinitCalled);
106 }
107 }
108
109 /********************************************************************************************************************/
110
111 template<typename Derived>
112 void InversionOfControlAccessor<Derived>::setMetaData(const std::optional<std::string>& name,
113 const std::optional<std::string>& unit, const std::optional<std::string>& description,
114 const std::optional<std::unordered_set<std::string>>& tags) {
115 std::optional<std::string> description_ = description;
116 if(description.has_value()) {
117 description_ = completeDescription(getOwner(), description.value());
118 }
119 _node.setMetaData(name, unit, description_, tags);
120 }
121
122 /********************************************************************************************************************/
123
124 template<typename Derived>
125 void InversionOfControlAccessor<Derived>::addTags(const std::unordered_set<std::string>& tags) {
126 for(const auto& tag : tags) {
127 _node.addTag(tag);
128 }
129 }
130
131 /********************************************************************************************************************/
132
133 template<typename Derived>
134 const std::unordered_set<std::string>& InversionOfControlAccessor<Derived>::getTags() {
135 return _node.getTags();
136 }
137
138 /********************************************************************************************************************/
139
140 template<typename Derived>
142 if(static_cast<Derived*>(this)->_impl != nullptr || other._impl != nullptr) {
143 try {
144 throw ChimeraTK::logic_error(
145 "Variable has been destroyed with active connections while application is still running");
146 }
147 catch(ChimeraTK::logic_error& ex) {
148 std::terminate();
149 }
150 }
151
152 // remove accessor from owning module
153 if(getOwner() != nullptr) {
154 getOwner()->unregisterAccessor(_node);
155 }
156
157 // remove node from model
158 if(getModel().isValid()) {
159 getModel().removeNode(_node);
160 }
161
162 // transfer the node
163 _node = std::move(other._node);
164 other._node = VariableNetworkNode(); // Make sure the destructor of other sees an invalid node
165
166 // update the app accessor pointer in the node
167 if(_node.getType() == NodeType::Application) {
168 _node.setAppAccessorPointer(static_cast<Derived*>(this));
169 }
170 else {
171 assert(_node.getType() == NodeType::invalid);
172 }
173 // Note: the accessor is registered by the VariableNetworkNode, so we don't have to re-register.
174
175 _initCalled = other._initCalled;
176 _deinitCalled = other._deinitCalled;
177 }
178
179 /********************************************************************************************************************/
180
181 template<typename Derived>
183 EntityOwner* owner, const std::string& description) const {
184 auto ownerDescription = owner->getFullDescription();
185 if(ownerDescription.empty()) {
186 return description;
187 }
188 if(description.empty()) {
189 return ownerDescription;
190 }
191 return ownerDescription + " - " + description;
192 }
193
194 /********************************************************************************************************************/
195
196 template<typename Derived>
198 VariableDirection direction, std::string unit, size_t nElements, UpdateMode mode, const std::string& description,
199 const std::type_info* valueType, const std::unordered_set<std::string>& tags)
200 : _node(owner, nullptr, ChimeraTK::Utilities::raiseIftrailingSlash(name, false), direction, unit, nElements, mode,
201 completeDescription(owner, description), valueType, tags) {
202 static_assert(std::is_base_of<InversionOfControlAccessor<Derived>, Derived>::value,
203 "InversionOfControlAccessor<> must be used in a curiously recurring template pattern!");
204
206
207 owner->registerAccessor(_node);
208 }
209
210 /********************************************************************************************************************/
211
212 template<typename Derived>
214 _initCalled = true;
215
216 /*
217 * This initialises pointer to App accessor. We cannot really do it in the constructor, since it then involves a
218 * static_cast into the App Accessor type, which is undefined behaviour at that point in time (base class
219 * constructor). This is found by ASAN.
220 */
221 _node.setAppAccessorPointer(static_cast<Derived*>(this));
222 }
223
224 /********************************************************************************************************************/
225
226 template<typename Derived>
228 _deinitCalled = true;
229
230 // This check and deregistration code involves "static_cast<Derived*>(this)" which is not allowed in the
231 // destructor any more - the derived part of the object is already gone. In the destructor there is no reliable way
232 // to get to the pointer to the implementation, since it is held by the other inheritance branch which may already
233 // be gone.
234 if(getOwner() != nullptr) {
235 if(static_cast<Derived*>(this)->_impl != nullptr) {
236 auto* entity = getOwner();
237
238 if(entity != nullptr) {
239 auto* owner = dynamic_cast<Module*>(entity);
240 while(owner->getOwner() != nullptr) {
241 owner = dynamic_cast<Module*>(owner->getOwner());
242 }
243
244 auto* application = dynamic_cast<Application*>(owner);
245 assert(application != nullptr);
246
247 if(application->getLifeCycleState() == LifeCycleState::run) {
248 try {
249 throw ChimeraTK::logic_error(
250 "Variable has been destroyed with active connections while application is "
251 "still running. Maybe the Application did not call shutdown() in its destructor?");
252 }
253 catch(ChimeraTK::logic_error&) {
254 std::terminate();
255 }
256 }
257 }
258 }
259 getOwner()->unregisterAccessor(_node);
260 }
261 if(getModel().isValid()) {
262 try {
263 getModel().removeNode(_node);
264 }
265 catch(ChimeraTK::logic_error& e) {
266 std::cerr << "ChimeraTK::logic_error caught: " << e.what() << std::endl;
267 std::terminate();
268 }
269 }
270 }
271
272 /********************************************************************************************************************/
273
274 template<typename Derived>
276 _node.registerInModel();
277 }
278
279 /********************************************************************************************************************/
280
281 template<typename Derived>
282 template<typename UserType>
284 auto& ndra = dynamic_cast<NDRegisterAccessorAbstractor<UserType>&>(*this);
285
286 // make sure to propagate initial values
287 if(ndra.getVersionNumber() == VersionNumber(nullptr)) {
288 return true;
289 }
290
291 // Need to get to the MetaDataPropagatingRegisterDecorator to obtain the last written data validity for this PV.
292 // The dynamic_cast is ok, since the MetaDataPropagatingRegisterDecorator is always the outermost accessor, cf.
293 // the data validity propagation specification, Section 2.5.1.
294 const auto& targetMetaDataPropagatingDecorator =
295 dynamic_cast<const MetaDataPropagatingRegisterDecorator<UserType>&>(*ndra.getImpl());
296
297 // Special case: propagation from module to output explicitly suppressed by tag
298 if(targetMetaDataPropagatingDecorator.getDisableDataValidityPropagation()) {
299 return targetMetaDataPropagatingDecorator.getTargetValidity() != ndra.dataValidity();
300 }
301
302 // Special case: DataValidity has been overridden for this PV to faulty
303 if(ndra.dataValidity() == DataValidity::faulty) {
304 // Last written DataValidity != faulty -> write is necessary
305 return targetMetaDataPropagatingDecorator.getTargetValidity() != DataValidity::faulty;
306 }
307
308 // Check with DataValidity from owning module
309 return targetMetaDataPropagatingDecorator.getTargetValidity() != this->getOwner()->getDataValidity();
310 }
311
312 /********************************************************************************************************************/
313
314} // namespace ChimeraTK
Base class for owners of other EntityOwners (e.g.
Definition EntityOwner.h:38
void registerAccessor(VariableNetworkNode accessor)
Called inside the constructor of Accessor: adds the accessor to the list.
virtual std::string getFullDescription() const =0
Obtain the full description including the full description of the owner.
Adds features required for inversion of control to an accessor.
void deinit()
Early deinitialisation of stuff we cannot do in our destructor.
void init()
Late initialisation of stuff we cannot do in our constructor.
void setMetaData(const std::optional< std::string > &name, const std::optional< std::string > &unit={}, const std::optional< std::string > &description={}, const std::optional< std::unordered_set< std::string > > &tags={})
Change meta data (name, unit, description and optionally tags).
void replace(Derived &&other)
Replace with other accessor.
EntityOwner * getOwner() const
Return the owning module.
bool checkMetadataWriteDifference()
Checks whether a writeIfDifferent would have to issue a write, even if the payload data is unchanged.
std::string completeDescription(EntityOwner *owner, const std::string &description) const
complete the description with the full description from the owner
void addTags(const std::unordered_set< std::string > &tags)
Add multiple tags.
const std::unordered_set< std::string > & getTags()
Return set of tags.
void registerInModel()
Register the variable in the model.
InversionOfControlAccessor()=default
Default constructor creates a dysfunctional accessor (to be assigned with a real accessor later)
InversionOfControlAccessor(Module *owner, const std::string &name, VariableDirection direction, std::string unit, size_t nElements, UpdateMode mode, const std::string &description, const std::type_info *valueType, const std::unordered_set< std::string > &tags={})
Constructor, only used by child class accessors.
virtual ~InversionOfControlAccessor()
Unregister at its owner when deleting.
void addTag(const std::string &tag)
Add a tag.
Model::ProcessVariableProxy getModel() const
NDRegisterAccessorDecorator which propagates meta data attached to input process variables through th...
Base class for ApplicationModule and DeviceModule, to have a common interface for these module types.
Definition Module.h:21
Class describing a node of a variable network.
Model::ProcessVariableProxy getModel() const
void addTag(const std::string &tag) const
Add a tag.
InvalidityTracer application module.
@ run
Actual run phase with full multi threading.
UpdateMode
Enum to define the update mode of variables.
Definition Flags.h:31
Struct to define the direction of variables.
Definition Flags.h:13