ChimeraTK-DeviceAccess 03.25.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>
182 bool LNMBackendVariableAccessor<UserType>::mayReplaceOther(const boost::shared_ptr<TransferElement const>&) const {
183 return false; // never replace, since it does not optimise anything
184 }
185
186 /********************************************************************************************************************/
187
188 template<typename UserType>
190 return _info.targetType == LNMBackendRegisterInfo::TargetType::CONSTANT;
191 }
192
193 /********************************************************************************************************************/
194
195 template<typename UserType>
197 return true;
198 }
199
200 /********************************************************************************************************************/
201
202 template<typename UserType>
204 return _info.targetType != LNMBackendRegisterInfo::TargetType::CONSTANT;
205 }
206
207 /********************************************************************************************************************/
208
209 template<typename UserType>
211 if(!_dev->_opened) {
212 throw ChimeraTK::logic_error("Cannot read from a closed device.");
213 }
214 }
215
216 /********************************************************************************************************************/
217
218 template<typename UserType>
220 if(!hasNewData) return;
221
222 if(!_flags.has(AccessMode::wait_for_new_data)) {
223 // poll-type read transfer: fetch latest value from ValueTable
224 auto& lnmVariable = _dev->_variables[_info.name];
225 std::lock_guard<std::mutex> lock(lnmVariable.valueTable_mutex);
226
227 callForType(_info.valueType, [&, this](auto arg) {
228 auto& vtEntry = boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table);
229 for(size_t i = 0; i < this->buffer_2D[0].size(); ++i) {
230 this->buffer_2D[0][i] = userTypeToUserType<UserType>(vtEntry.latestValue[i + _wordOffsetInRegister]);
231 }
232 this->_dataValidity = vtEntry.latestValidity;
233 // Note: passing through the version number also for push-type variables is essential for the MathPlugin (cf.
234 // MathPluginFormulaHelper::checkAllParametersWritten()) and does not violate the spec (spec says we should not
235 // be able to see whether there was an update, which still is impossible since updates can have the same
236 // version number as before).
237 this->_versionNumber = vtEntry.latestVersion;
238 });
239 }
240 else {
241 // push-type read transfer: received value is in _queueValue (cf. readQueue continuation in constructor)
242 this->buffer_2D[0].swap(_queueValue.value);
243 this->_versionNumber = _queueValue.version;
244 this->_dataValidity = _queueValue.validity;
245 }
246 }
247
248 /********************************************************************************************************************/
249
250 template<typename UserType>
252 auto& lnmVariable = _dev->_variables[_info.name];
253 std::lock_guard<std::mutex> lock(lnmVariable.valueTable_mutex);
254
255 callForType(_info.valueType, [&, this](auto arg) {
256 auto& vtEntry = boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table);
257 auto itsub = vtEntry.subscriptions.find(this->getId());
258 if(itsub != vtEntry.subscriptions.end()) {
259 this->interrupt_impl(itsub->second);
260 }
261 });
262 }
263
264 /********************************************************************************************************************/
265
267} // 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.