ChimeraTK-DeviceAccess 03.29.00
Loading...
Searching...
No Matches
LNMBackendVariableAccessor.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
3
5
7
8namespace ChimeraTK {
9
10 /********************************************************************************************************************/
11
12 template<typename UserType>
13 LNMBackendVariableAccessor<UserType>::LNMBackendVariableAccessor(const boost::shared_ptr<DeviceBackend>& dev,
14 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
15 : NDRegisterAccessor<UserType>(registerPathName, flags), _registerPathName(registerPathName),
16 _wordOffsetInRegister(wordOffsetInRegister), _flags(flags) {
17 // cast device
18 _dev = boost::dynamic_pointer_cast<LogicalNameMappingBackend>(dev);
19
20 // obtain the register info
21 _info = _dev->_catalogue_mutable.getBackendRegister(_registerPathName);
22 // boost::static_pointer_cast<BackendRegisterCatalogue<LNMBackendRegisterInfo>>(_dev->getRegisterCatalogue().getRegister(_registerPathName));
23
24 // check for unknown flags
27 }
28 else {
29 // no flags are supported for constants
30 flags.checkForUnknownFlags({});
31 }
32
33 // numberOfWords default to full register length
34 if(numberOfWords == 0) numberOfWords = _info.length;
35
36 // check for illegal parameter combinations
37 if(wordOffsetInRegister + numberOfWords > _info.length) {
39 "Requested number of words and/or offset exceeds length of register '" + registerPathName + "'.");
40 }
41
42 // check for incorrect usage of this accessor
45 throw ChimeraTK::logic_error("LNMBackendVariableAccessor used for wrong register type."); // LCOV_EXCL_LINE
46 // (impossible to
47 // test...)
48 }
49
50 // if wait_for_new_data is specified, make subscription
52 // allocate _queueValue buffer
53 this->_queueValue.value.resize(numberOfWords);
54
55 auto& lnmVariable = _dev->_variables[_info.name];
56 std::lock_guard<std::mutex> lock(lnmVariable.valueTable_mutex);
57
58 callForType(_info.valueType, [&, this](auto arg) {
59 using T = decltype(arg);
60 auto& vtEntry = boost::fusion::at_key<T>(lnmVariable.valueTable.table);
61 // create subscription queue
62 cppext::future_queue<typename LNMVariable::ValueTable<T>::QueuedValue> queue(3);
63 // place queue in map
64 vtEntry.subscriptions[this->getId()] = queue;
65 // make void-typed continuation of subscription queue, which stores the received value into the _queueValue
66 this->_readQueue = queue.template then<void>(
67 [this](const typename LNMVariable::ValueTable<T>::QueuedValue& queueValue) {
68 this->_queueValue.validity = queueValue.validity;
69 this->_queueValue.version = queueValue.version;
70 for(size_t i = 0; i < queueValue.value.size(); ++i) {
71 this->_queueValue.value[i] =
72 userTypeToUserType<UserType>(queueValue.value[i + this->_wordOffsetInRegister]);
73 }
74 },
75 std::launch::deferred);
76 // put initial value into queue, if async reads activated
77 if(_dev->_asyncReadActive) {
78 queue.push({vtEntry.latestValue, vtEntry.latestValidity, vtEntry.latestVersion});
79 }
80 });
81 }
82
83 // make sure FormulaHelpers for MathPlugin instances involving this variable as push-parameter are created
84 auto& lnmVariable = _dev->_variables[_info.name];
85 for(auto* mp : lnmVariable.usingFormulas) {
86 if(mp->_hasPushParameter) {
87 // following check eliminates recursion, getFormulaHelper also creates Variable accessors
88 if(!mp->_creatingFormulaHelper) {
89 auto h = mp->getFormulaHelper(_dev);
90 _formulaHelpers.push_back(h);
91 }
92 }
93 }
94
95 // allocate application buffer
97 NDRegisterAccessor<UserType>::buffer_2D[0].resize(numberOfWords);
98 }
99
100 /********************************************************************************************************************/
101
102 template<typename UserType>
104 if(_flags.has(AccessMode::wait_for_new_data)) {
105 // unsubscribe the update queue
106 auto& lnmVariable = _dev->_variables[_info.name];
107 std::lock_guard<std::mutex> lock(lnmVariable.valueTable_mutex);
108 callForType(_info.valueType, [&, this](auto arg) {
109 using T = decltype(arg);
110 auto& vtEntry = boost::fusion::at_key<T>(lnmVariable.valueTable.table);
111 vtEntry.subscriptions.erase(this->getId());
112 });
113 }
114 }
115
116 /********************************************************************************************************************/
117
118 template<typename UserType>
120 _dev->checkActiveException();
121 }
122
123 /********************************************************************************************************************/
124
125 template<typename UserType>
127 std::ignore = type;
130 "Writing to constant-type registers of logical name mapping devices is not possible.");
131 }
132 if(!_dev->_opened) { // directly use member variables as friend to avoid virtual function calls
133 throw ChimeraTK::logic_error("Cannot write to a closed device.");
134 }
135 }
136
137 /********************************************************************************************************************/
138
139 template<typename UserType>
141 _dev->checkActiveException();
142 auto& lnmVariable = _dev->_variables[_info.name];
143 std::lock_guard<std::mutex> lock(lnmVariable.valueTable_mutex);
144
145 callForType(_info.valueType, [&, this](auto arg) {
146 auto& vtEntry = boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table);
147
148 // store new value as latest value
149 for(size_t i = 0; i < this->buffer_2D[0].size(); ++i) {
150 vtEntry.latestValue[i + _wordOffsetInRegister] = userTypeToUserType<decltype(arg)>(this->buffer_2D[0][i]);
151 }
152 vtEntry.latestValidity = this->dataValidity();
153 vtEntry.latestVersion = v;
154
155 // push new value to subscription queues, if async read is activated
156 if(_dev->_asyncReadActive) {
157 for(auto& sub : vtEntry.subscriptions) {
158 sub.second.push_overwrite({vtEntry.latestValue, this->dataValidity(), v});
159 }
160 }
161 });
162 return false;
163 }
164
165 /********************************************************************************************************************/
166
167 template<typename UserType>
169 TransferType /*type*/, ChimeraTK::VersionNumber versionNumber) {
170 // call write functions which make use of this parameter in MathPlugin-handled formulas
171 for(const auto& h : _formulaHelpers) {
172 h->updateResult(versionNumber);
173 // error handling: updateResult does it already.
174 // we don't want to issue exceptions from VariableAccessor, since a variable change is not closely related
175 // to where the error appears (e.g. error appears when writing to target)
176 }
177 }
178
179 /********************************************************************************************************************/
180
181 template<typename UserType>
183 const boost::shared_ptr<TransferElement const>& other) const {
184 auto rhsCasted = boost::dynamic_pointer_cast<const LNMBackendVariableAccessor<UserType>>(other);
185 if(rhsCasted.get() == this) {
186 return false;
187 }
188 if(!rhsCasted) {
189 return false;
190 }
191 if(_dev != rhsCasted->_dev) {
192 return false;
193 }
194 if(_info != rhsCasted->_info) {
195 return false;
196 }
197 if(_wordOffsetInRegister != rhsCasted->_wordOffsetInRegister) {
198 return false;
199 }
200 if(_flags != rhsCasted->_flags) {
201 return false;
202 }
203
204 // We do not compare formula helpers because they are constructed from the _info, and hence are equal if the _infos
205 // are equal.
206 return true;
207 }
208
209 /********************************************************************************************************************/
210
211 template<typename UserType>
213 return _info.targetType == LNMBackendRegisterInfo::TargetType::CONSTANT;
214 }
215
216 /********************************************************************************************************************/
217
218 template<typename UserType>
220 return true;
221 }
222
223 /********************************************************************************************************************/
224
225 template<typename UserType>
227 return _info.targetType != LNMBackendRegisterInfo::TargetType::CONSTANT;
228 }
229
230 /********************************************************************************************************************/
231
232 template<typename UserType>
234 if(!_dev->_opened) {
235 throw ChimeraTK::logic_error("Cannot read from a closed device.");
236 }
237 }
238
239 /********************************************************************************************************************/
240
241 template<typename UserType>
243 if(!hasNewData) return;
244
245 if(!_flags.has(AccessMode::wait_for_new_data)) {
246 // poll-type read transfer: fetch latest value from ValueTable
247 auto& lnmVariable = _dev->_variables[_info.name];
248 std::lock_guard<std::mutex> lock(lnmVariable.valueTable_mutex);
249
250 callForType(_info.valueType, [&, this](auto arg) {
251 auto& vtEntry = boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table);
252 for(size_t i = 0; i < this->buffer_2D[0].size(); ++i) {
253 this->buffer_2D[0][i] = userTypeToUserType<UserType>(vtEntry.latestValue[i + _wordOffsetInRegister]);
254 }
255 this->_dataValidity = vtEntry.latestValidity;
256 // Note: passing through the version number also for push-type variables is essential for the MathPlugin (cf.
257 // MathPluginFormulaHelper::checkAllParametersWritten()) and does not violate the spec (spec says we should not
258 // be able to see whether there was an update, which still is impossible since updates can have the same
259 // version number as before).
260 this->_versionNumber = vtEntry.latestVersion;
261 });
262 }
263 else {
264 // push-type read transfer: received value is in _queueValue (cf. readQueue continuation in constructor)
265 this->buffer_2D[0].swap(_queueValue.value);
266 this->_versionNumber = _queueValue.version;
267 this->_dataValidity = _queueValue.validity;
268 }
269 }
270
271 /********************************************************************************************************************/
272
273 template<typename UserType>
275 auto& lnmVariable = _dev->_variables[_info.name];
276 std::lock_guard<std::mutex> lock(lnmVariable.valueTable_mutex);
277
278 callForType(_info.valueType, [&, this](auto arg) {
279 auto& vtEntry = boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table);
280 auto itsub = vtEntry.subscriptions.find(this->getId());
281 if(itsub != vtEntry.subscriptions.end()) {
282 this->interrupt_impl(itsub->second);
283 }
284 });
285 }
286
287 /********************************************************************************************************************/
288
290} // namespace ChimeraTK
#define INSTANTIATE_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass)
Set of AccessMode flags with additional functionality for an easier handling.
Definition AccessMode.h:48
bool has(AccessMode flag) const
Check if a certain flag is in the set.
Definition AccessMode.cc:20
void checkForUnknownFlags(const std::set< AccessMode > &knownFlags) const
Check of any flag which is not in the given set "knownFlags" is set.
Definition AccessMode.cc:32
DataType valueType
Data type of CONSTANT or VARIABLE type.
unsigned int length
The length of the range (i.e.
TargetType targetType
Type of the target.
RegisterPath name
Name of the register.
LNMBackendVariableAccessor(const boost::shared_ptr< DeviceBackend > &dev, const RegisterPath &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
LNMBackendRegisterInfo _info
register information.
boost::shared_ptr< LogicalNameMappingBackend > _dev
backend device
RegisterPath _registerPathName
register and module name
LNMVariable::ValueTable< UserType >::QueuedValue _queueValue
Intermediate buffer used when receiving value from queue, as writing to application buffer must only ...
N-dimensional register accessor.
std::vector< std::vector< UserType > > buffer_2D
Buffer of converted data elements.
Class to store a register path name.
Class for generating and holding version numbers without exposing a numeric representation.
Exception thrown when a logic error has occured.
Definition Exception.h:51
void callForType(const std::type_info &type, LAMBDATYPE lambda)
Helper function for running code which uses some compile-time type that is specified at runtime as a ...
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
TransferType
Used to indicate the applicable operation on a Transferelement.