ChimeraTK-DeviceAccess  03.18.00
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 
8 namespace 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
25  if(_info.targetType == LNMBackendRegisterInfo::TargetType::VARIABLE) {
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
43  if(_info.targetType != LNMBackendRegisterInfo::TargetType::CONSTANT &&
44  _info.targetType != LNMBackendRegisterInfo::TargetType::VARIABLE) {
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 
266  INSTANTIATE_TEMPLATE_FOR_CHIMERATK_USER_TYPES(LNMBackendVariableAccessor);
267 } // namespace ChimeraTK
ChimeraTK::LNMBackendVariableAccessor::_info
LNMBackendRegisterInfo _info
register information.
Definition: LNMBackendVariableAccessor.h:59
ChimeraTK::LNMBackendRegisterInfo::name
RegisterPath name
Name of the registrer.
Definition: LNMBackendRegisterInfo.h:47
ChimeraTK::NDRegisterAccessor::buffer_2D
std::vector< std::vector< UserType > > buffer_2D
Buffer of converted data elements.
Definition: NDRegisterAccessor.h:123
LNMMathPluginFormulaHelper.h
ChimeraTK::AccessModeFlags::has
bool has(AccessMode flag) const
Check if a certain flag is in the set.
Definition: AccessMode.cc:20
ChimeraTK::AccessMode::wait_for_new_data
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
INSTANTIATE_TEMPLATE_FOR_CHIMERATK_USER_TYPES
#define INSTANTIATE_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass)
Definition: SupportedUserTypes.h:497
ChimeraTK::LNMBackendVariableAccessor::LNMBackendVariableAccessor
LNMBackendVariableAccessor(const boost::shared_ptr< DeviceBackend > &dev, const RegisterPath &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Definition: LNMBackendVariableAccessor.cc:13
ChimeraTK::TransferType
TransferType
Used to indicate the applicable operation on a Transferelement.
Definition: TransferElement.h:51
ChimeraTK::LNMBackendRegisterInfo::targetType
TargetType targetType
Type of the target.
Definition: LNMBackendRegisterInfo.h:50
ChimeraTK::AccessModeFlags::checkForUnknownFlags
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
ChimeraTK::LNMBackendRegisterInfo::valueType
DataType valueType
Data type of CONSTANT or VARIABLE type.
Definition: LNMBackendRegisterInfo.h:74
ChimeraTK::LNMBackendVariableAccessor::_queueValue
LNMVariable::ValueTable< UserType >::QueuedValue _queueValue
Intermediate buffer used when receiving value from queue, as writing to application buffer must only ...
Definition: LNMBackendVariableAccessor.h:66
ChimeraTK::callForType
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 ...
Definition: SupportedUserTypes.h:919
ChimeraTK::RegisterPath
Class to store a register path name.
Definition: RegisterPath.h:16
ChimeraTK::LNMBackendVariableAccessor::_dev
boost::shared_ptr< LogicalNameMappingBackend > _dev
backend device
Definition: LNMBackendVariableAccessor.h:55
ChimeraTK::VersionNumber
Class for generating and holding version numbers without exposing a numeric representation.
Definition: VersionNumber.h:23
ChimeraTK::LNMBackendRegisterInfo::length
unsigned int length
The length of the range (i.e.
Definition: LNMBackendRegisterInfo.h:62
LNMBackendVariableAccessor.h
ChimeraTK::AccessModeFlags
Set of AccessMode flags with additional functionality for an easier handling.
Definition: AccessMode.h:48
ChimeraTK::LNMBackendVariableAccessor
Definition: LNMBackendVariableAccessor.h:21
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::LNMBackendVariableAccessor::_registerPathName
RegisterPath _registerPathName
register and module name
Definition: LNMBackendVariableAccessor.h:52
ChimeraTK::NDRegisterAccessor
N-dimensional register accessor.
Definition: ForwardDeclarations.h:17
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51