ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
LNMMathPlugin.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
4#include "LNMMathPlugin.h"
5
6#include "BackendFactory.h"
10#include "TransferElement.h"
11
12#include <ChimeraTK/cppext/finally.hpp>
13
14namespace ChimeraTK::LNMBackend {
15
16 thread_local int64_t MathPlugin::_writeLockCounter = 0;
17
18 /********************************************************************************************************************/
19
21 const LNMBackendRegisterInfo& info, size_t pluginIndex, std::map<std::string, std::string> parameters)
22 : AccessorPlugin(info, pluginIndex), _parameters(std::move(parameters)) {
23 // extract parameters
24 if(_parameters.find("formula") == _parameters.end()) {
25 throw ChimeraTK::logic_error("LogicalNameMappingBackend MultiplierPlugin: Missing parameter 'formula'.");
26 }
27 _formula = _parameters.at("formula");
28 _parameters.erase("formula");
29 if(_parameters.find("enable_push_parameters") != _parameters.end()) {
31 _parameters.erase("enable_push_parameters");
32 }
33 }
34
35 /********************************************************************************************************************/
36
38 // Change data type to non-integral
41
42 // Fix to unidirectional operation
44 _info.readable = false;
45 }
46
48
49 if(!_isWrite) { // reading MathPlugin
52 "MathPlugin (reading) does not support push_parameters: " + _info.getRegisterName());
53 }
54 }
55 }
56
57 /********************************************************************************************************************/
58
59 void MathPlugin::openHook(const boost::shared_ptr<LogicalNameMappingBackend>& backend) {
60 // make sure backend catalogue is updated with target backend information
61 const auto& catalogue = backend->_catalogue_mutable;
62
63 // produce logic_error if MathPlugin has insufficient access rights to parameters
64 if(_isWrite && !_info.isWriteable()) {
65 throw ChimeraTK::logic_error("MathPlugin target not writeable:" + _info.name);
66 }
67 for(const auto& parpair : _parameters) {
68 auto r = catalogue.getRegister(parpair.second);
69
70 if(!r.isReadable()) {
71 throw ChimeraTK::logic_error("MathPlugin parameter not readable:" + parpair.second);
72 }
73 }
74
75 // we require that all values used in the formula need to be written after open, before we provide first result
76 {
77 std::unique_lock<std::recursive_mutex> lk(_writeMutex);
80 }
81 }
82
83 /********************************************************************************************************************/
84
85 void MathPlugin::postParsingHook(const boost::shared_ptr<const LogicalNameMappingBackend>& backend) {
86 // whether this plugin is write mode depends on catalogue of target device so we need to update catalogue
87 // note, some target devices (e.g. DOOCS backend) provide their catalogue only on open, so it's not final here.
88 auto catalogue = backend->getRegisterCatalogue();
89 if(_isWrite) {
90 // Write direction: check that we have only lnm defined variables as parameters
91 // Current push implementation (via LNMBackendVariableAccessor<UserType>::doPostWrite) is only for variables.
92 // Therefore, we agreed to disallow non-variables, even when push-feature not requested.
93 for(const auto& parpair : _parameters) {
94 std::string pname = RegisterPath(parpair.second); // conversion to RegisterPath and back adds leading '/'
95 if(backend->_variables.find(pname) == backend->_variables.end()) {
96 throw logic_error("no LNM variable defined for parameter " + pname);
97 }
98 // set up connections for push-enabled variables. It is important that we do this already before openHook,
99 // since RegisterAccessor to Variable can be requested earlier!
101 _hasPushParameter = true;
102 backend->_variables[pname].usingFormulas.insert(this);
103 }
104 }
106 throw ChimeraTK::logic_error("MathPlugin (writing) with push_parameters requested but there is no parameter!");
107 }
108 }
109 }
110
111 /********************************************************************************************************************/
112
113 boost::shared_ptr<MathPluginFormulaHelper> MathPlugin::getFormulaHelper(
114 boost::shared_ptr<LogicalNameMappingBackend> backend) {
115 // lock against concurrent creation
116 std::unique_lock<std::recursive_mutex> lk{_writeMutex};
117 auto p = _h.lock();
118 if(!p) {
119 if(!backend) {
120 return nullptr;
121 }
123 p = boost::make_shared<MathPluginFormulaHelper>(this, backend);
125 _h = p;
126 }
127 return p;
128 }
129
130 /********************************************************************************************************************/
131
133 auto paramDataValidity = ChimeraTK::DataValidity::ok;
134 for(auto& p : params) {
135 p.first->readLatest();
136 if(p.first->dataValidity() == ChimeraTK::DataValidity::faulty) {
137 // probably compiler optimize it automatically and assign it only once.
138 paramDataValidity = ChimeraTK::DataValidity::faulty;
139 }
140 }
141 return paramDataValidity;
142 }
143
144 /********************************************************************************************************************/
145
147 // Note: _target might be invalid until _mp->_mainValueWrittenAfterOpen == true
148
149 try {
150 std::unique_lock<std::recursive_mutex> lk(_mp->_writeMutex);
151
152 auto paramDataValidity = updateParameters();
153
155 return;
156 }
157
158 assert(_mp->_lastMainValue.size() == _target->getNumberOfSamples());
159
160 computeResult(_mp->_lastMainValue, _target->accessChannel(0));
161 // pass validity to target and delegate preWrite
163 _target->setDataValidity(ChimeraTK::DataValidity::ok);
164 }
165 else {
167 }
168
169 // if versionNumber at target register is already greater, take it instead of supplied versionNumber
170 auto writeVs = std::max<ChimeraTK::VersionNumber>(versionNumber, _target->getVersionNumber());
171 _target->writeDestructively(writeVs);
172 }
173 catch(runtime_error& ex) {
174 // runtime_error from param.readLatest() or target->write()
175 // we could actually even ignore it, since we don't expect exceptions on param.readLatest(), and target->write()
176 // already puts backend into exception state.
177 _target->getExceptionBackend()->setException(ex.what());
178 }
179 }
180
181 /********************************************************************************************************************/
182
184
185 /********************************************************************************************************************/
186
190
191 /********************************************************************************************************************/
192
195 return true;
196 }
197
198 for(const auto& acc : _accessorMap) {
199 // Version number check will notice whether valid data has been provided after open. This works for both push and
200 // poll, since the LNM variables always pass through the version number from the write operation to the read.
201 if(acc.second->getVersionNumber() <= _backend->getVersionOnOpen()) {
202 return false;
203 }
204 }
207 }
208
209 /********************************************************************************************************************/
210
211 template<typename UserType>
214
215 MathPluginDecorator(boost::shared_ptr<LogicalNameMappingBackend>& backend,
216 const boost::shared_ptr<ChimeraTK::NDRegisterAccessor<double>>& target, MathPlugin* p);
217
218 void doPreRead(TransferType type) override {
219 if(_p->_isWrite) {
220 throw ChimeraTK::logic_error("This register with MathPlugin enabled is not readable: " + _target->getName());
221 }
222 _target->preRead(type);
223 }
224
225 // registers are either readable or writeable
226 [[nodiscard]] bool isReadable() const override { return !_p->_isWrite; }
227
228 void doPostRead(TransferType type, bool hasNewData) override;
229
230 void doPreWrite(TransferType type, VersionNumber versionNumber) override;
231
232 void doPostWrite(TransferType type, VersionNumber versionNumber) override;
233
236
237 boost::shared_ptr<MathPluginFormulaHelper> _h;
239
240 // If not all parameters have been updated in the plugin, all parts of the write
241 // transaction (preWrite, writeTransfer and postWrite) are not delegated to the target
243
244 using ChimeraTK::NDRegisterAccessorDecorator<UserType, double>::_target;
245
246 void setExceptionBackend(boost::shared_ptr<DeviceBackend>) override;
247 };
248
249 /********************************************************************************************************************/
250
251 template<typename UserType>
252 MathPluginDecorator<UserType>::MathPluginDecorator(boost::shared_ptr<LogicalNameMappingBackend>& backend,
253 const boost::shared_ptr<ChimeraTK::NDRegisterAccessor<double>>& target, MathPlugin* p)
254 : ChimeraTK::NDRegisterAccessorDecorator<UserType, double>(target), _p(p) {
255 // 2D arrays are not yet supported
256 if(_target->getNumberOfChannels() != 1) {
258 "The LogicalNameMapper MathPlugin supports only scalar or 1D array registers. Register name: " +
259 this->getName());
260 }
261
262 // Although this is a decorator we do not use the exceptions backend from the target because this decorator is
263 // given out by a backend
264 target->setExceptionBackend(backend);
265 this->_exceptionBackend = backend;
266
267 _h = _p->getFormulaHelper(backend);
268 }
269
270 /********************************************************************************************************************/
271
272 template<typename UserType>
274 _target->postRead(type, hasNewData);
275 if(!hasNewData) return;
276
277 auto paramDataValidity = _h->updateParameters();
278
279 // evaluate the expression and store into application buffer
280 _h->computeResult(_target->accessChannel(0), buffer_2D[0]);
281
282 // update version number and validity from target
283 this->_versionNumber = _target->getVersionNumber();
284 this->_dataValidity = _target->dataValidity();
285 if(paramDataValidity == ChimeraTK::DataValidity::faulty) {
286 this->_dataValidity = ChimeraTK::DataValidity::faulty;
287 }
288 }
289
290 /********************************************************************************************************************/
291
292 boost::shared_ptr<LogicalNameMappingBackend> MathPluginFormulaHelper::getBackend() {
293 return _backend;
294 }
295
296 /********************************************************************************************************************/
297
298 template<typename UserType>
300 if(!_p->_isWrite) {
301 throw ChimeraTK::logic_error("This register with MathPlugin enabled is not writeable: " + _target->getName());
302 }
303 // LNM backend
304 auto backend = _h->getBackend();
305 if(!backend->isOpen()) {
306 throw ChimeraTK::logic_error("LNM backend not opened!");
307 }
308 backend->checkActiveException();
309
310 // The readLatest might throw an exception. In this case preWrite() is never delegated and we must not call the
311 // target's postWrite().
312 _skipWriteDelegation = true;
313
314 auto paramDataValidity = _h->updateParameters();
315
316 // convert from UserType to double - use the target accessor's buffer as a temporary buffer (this is a bit a hack,
317 // but it is safe to overwrite the buffer and we can avoid the need for an additional permanent buffer which might
318 // not even be used if the register is never written).
319 for(size_t k = 0; k < buffer_2D[0].size(); ++k) {
320 _target->accessData(0, k) = userTypeToNumeric<double>(buffer_2D[0][k]);
321 }
322
323 // update last written data buffer for other threads if needed
324 if(_p->_hasPushParameter) {
325 // Accquire the lock and hold it until the transaction is completed in postWrite.
326 // This is safe because it is guaranteed by the framework that pre- and post actions are called in pairs.
327 // Do this before the first call to the target, which might create its own locks.
328 _p->_writeMutex.lock();
329 // preWrite() might be called multiple times before postWrite() is called. There are multiple conditions
330 // whether the writeMutex is locked (_hasPushParameters, _skipWriteDelegation, exceptions) so we count
331 // separately how many times the lock has been aquired, so we can release it the exact right amount of times.
332 ++(_p->_writeLockCounter);
333 _p->_lastMainValue = _target->accessChannel(0);
334 _p->_lastMainValidity = _target->dataValidity();
335 _p->_mainValueWrittenAfterOpen = true;
336
337 if(!_h->checkAllParametersWritten()) {
338 return;
339 }
340 }
341
342 // There either are only poll-type parameters, or all push-type parameters have been received.
343 // We are OK to go through with the transfer
344 _skipWriteDelegation = false;
345
346 // evaluate the expression and store into target accessor
347 _h->computeResult(_target->accessChannel(0), _target->accessChannel(0));
348
349 // pass validity to target and delegate preWrite
350 if(paramDataValidity == ChimeraTK::DataValidity::ok && this->_dataValidity == ChimeraTK::DataValidity::ok) {
351 _target->setDataValidity(ChimeraTK::DataValidity::ok);
352 }
353 else {
354 _target->setDataValidity(ChimeraTK::DataValidity::faulty);
355 }
356 // if versionNumber at target register is already greater, take it instead of supplied versionNumber
357 auto writeVs = std::max<ChimeraTK::VersionNumber>(versionNumber, _target->getVersionNumber());
358 _target->preWrite(type, writeVs);
359 }
360
361 /********************************************************************************************************************/
362
363 template<typename UserType>
365 if(_skipWriteDelegation) {
366 return false; // No data loss. Value has been stored in preWrite for the parameters thread.
367 }
368
369 return _target->writeTransfer(versionNumber);
370 }
371
372 /********************************************************************************************************************/
373
374 template<typename UserType>
376 return doWriteTransfer(versionNumber);
377 }
378
379 /********************************************************************************************************************/
380
381 template<typename UserType>
383 // Make sure the mutex is released, even if the delegated postWrite kicks out with an exception.
384 // This has to happen at the very end, after all delegations, such that the target can release all its internal locks first.
385 auto _ = cppext::finally([&] {
386 if(_p->_writeLockCounter > 0) {
387 --(_p->_writeLockCounter);
388 _p->_writeMutex.unlock();
389 }
390 });
391
392 if(_skipWriteDelegation && (this->_activeException != nullptr)) {
393 // Something has thrown before the target's preWrite was called. Re-throw it here.
394 // Do not unlock the mutex. It never has been locked.
395 std::rethrow_exception(this->_activeException);
396 }
397
398 if(_skipWriteDelegation) {
399 return; // the trarget preWrite() has not been executed, so stop here
400 }
401
402 // delegate to the target
403 _target->setActiveException(this->_activeException);
404 _target->postWrite(type, versionNumber);
405 // (the "finally" lambda releasing the lock is executed here latest)
406 } // namespace LNMBackend
407
408 /********************************************************************************************************************/
409
410 template<typename UserType>
411 void MathPluginDecorator<UserType>::setExceptionBackend(boost::shared_ptr<DeviceBackend> exceptionBackend) {
412 this->_exceptionBackend = exceptionBackend;
413 _target->setExceptionBackend(exceptionBackend);
414 _h->setExceptionBackend(exceptionBackend);
415 }
416
417 /********************************************************************************************************************/
418
419 void MathPluginFormulaHelper::setExceptionBackend(boost::shared_ptr<DeviceBackend> exceptionBackend) {
420 for(auto& p : params) {
421 p.first->setExceptionBackend(exceptionBackend);
422 }
423 }
424
425 /********************************************************************************************************************/
426
427 template<typename UserType, typename TargetType>
428 boost::shared_ptr<NDRegisterAccessor<UserType>> MathPlugin::decorateAccessor(
429 boost::shared_ptr<LogicalNameMappingBackend>& backend, boost::shared_ptr<NDRegisterAccessor<TargetType>>& target,
430 const UndecoratedParams&) {
431 if constexpr(std::is_same_v<TargetType, double>) {
432 return boost::make_shared<MathPluginDecorator<UserType>>(backend, target, this);
433 }
434 else {
435 // this code branch is compiled because of mpl for loop over types, but must never run
436 assert(false);
437 return {};
438 }
439 }
440
441 /********************************************************************************************************************/
442
444 MathPlugin* p, const boost::shared_ptr<LogicalNameMappingBackend>& backend)
445 : _backend(backend), _mp(p) {
446 auto* info = _mp->info();
447 auto length = info->length;
448
449 _target = backend->getRegisterAccessor_impl<double>(info->getRegisterName(), 0, 0, {}, _mp->_pluginIndex + 1);
450
451 for(const auto& parpair : _mp->_parameters) {
452 // Even push-type parameters should not be obtained with wait_for_new_data, since the variable accessor will
453 // trigger the math plugin update in its postWrite in that case. Using wait_for_new_data here would leave
454 // exceptions in the queues across recovery which would bring the backend into the exception state again.
455 auto acc = backend->getRegisterAccessor<double>(parpair.second, 0, 0, {});
456 if(acc->getNumberOfChannels() != 1) {
458 "The LogicalNameMapper MathPlugin supports only scalar or 1D array registers. Register name: '" +
459 info->name + "', parameter name: '" + parpair.first + "'");
460 }
461 _accessorMap[parpair.first] = acc;
462 }
464 _mp->_lastMainValue.resize(length);
465 }
466
467 // compile formula
469 varName = info->name;
470 }
471
472 /********************************************************************************************************************/
473
474 void MathPluginFormulaHelper::compileFormula(const std::string& formula,
475
476 const std::map<std::string, boost::shared_ptr<ChimeraTK::NDRegisterAccessor<double>>>& parameters,
477 size_t nElements) {
478 // create exprtk parser
479 exprtk::parser<double> parser;
480
481 // add basic constants like pi
482 symbols.add_constants();
483
484 // Add vector manipulation functions
485 symbols.add_package(vecOpsPkg);
486
487 // Create vector view for the value and add it to the symbol table. We need to use a vector view instead of adding
488 // the buffer directly as a vector, since our buffers might be swapped and hence the address of the data can
489 // change. The pointer used for the view is for now to a temporary vector and will become invalid once this
490 // function returns. This is acceptable, since before using the view the pointer will be updated to the right
491 // buffer. Using a nullptr instead of the temporary buffer does not work, as the buffer seems to be accessible
492 // during compilation of the formula.
493 std::vector<double> temp(nElements);
494 valueView = std::make_unique<exprtk::vector_view<double>>(exprtk::make_vector_view(temp, nElements));
495 symbols.add_vector("x", *valueView);
496
497 // iterate parameters, add all but 'formula' parameter as a variable.
498 for(const auto& parpair : parameters) {
499 if(parpair.first == "formula") continue;
500 const auto& acc = parpair.second;
501 acc->setExceptionBackend(_backend);
502 if(acc->getNumberOfChannels() != 1) {
504 "The LogicalNameMapper MathPlugin supports only scalar or 1D array registers. Register name: '" + varName +
505 "', parameter name: '" + parpair.first + "'");
506 }
507 auto view = std::make_unique<exprtk::vector_view<double>>(
508 exprtk::make_vector_view(acc->accessChannel(0), acc->getNumberOfSamples()));
509 symbols.add_vector(parpair.first, *view);
510 params[acc] = std::move(view);
511 }
512
513 // compile the expression
514 expression.register_symbol_table(symbols);
515 bool success = parser.compile(formula, expression);
516 if(!success) {
517 throw ChimeraTK::logic_error("LogicalNameMapping MathPlugin for register '" + varName +
518 "': failed to compile expression '" + formula + "': " + parser.error());
519 }
520 }
521
522 /********************************************************************************************************************/
523
524 template<typename T>
525 void MathPluginFormulaHelper::computeResult(std::vector<double>& x, std::vector<T>& resultBuffer) {
526 // inform the value view of the new data pointer - the buffer might have been swapped
527 valueView->rebase(x.data());
528
529 // update parameter buffers
530 for(auto& p : params) {
531 p.second->rebase(p.first->accessChannel(0).data());
532 }
533
534 // evaluate the expression, obtain the result in a way so it also works when using the return statement
535 double valueWhenNotUsingReturn = expression.value();
536 exprtk::results_context<double> results = expression.results();
537
538 // extract result depending on how it was returned
539 if(results.count() == 0) {
540 // if results.count() is 0, the return statement presumably has not been used
541 if(resultBuffer.size() != 1) {
542 throw ChimeraTK::runtime_error("LogicalNameMapping MathPlugin for register '" + varName +
543 "': The expression returns a scalar but " + std::to_string(resultBuffer.size()) + " expected.");
544 }
545 resultBuffer[0] = numericToUserType<T>(valueWhenNotUsingReturn);
546 }
547 else if(results.count() == 1) {
548 // return statement has been used to return exactly one value (note: this value might be an array)
549 exprtk::type_store<double> result = results[0];
550
551 // make sure we got a numeric result
552 if(result.type != exprtk::type_store<double>::e_scalar && result.type != exprtk::type_store<double>::e_vector) {
553 throw ChimeraTK::runtime_error("LogicalNameMapping MathPlugin for register '" + varName +
554 "': The expression did not return a numeric result.");
555 }
556
557 // create vector view and check that its size matches
558 exprtk::type_store<double>::type_view<double> view(result);
559 if(view.size() != resultBuffer.size()) {
560 throw ChimeraTK::runtime_error("LogicalNameMapping MathPlugin for register '" + varName +
561 "': The expression returns " + std::to_string(view.size()) + " elements but " +
562 std::to_string(resultBuffer.size()) + " expected.");
563 }
564
565 // convert and copy data into target buffer
566 for(size_t k = 0; k < view.size(); ++k) {
567 resultBuffer[k] = numericToUserType<T>(view[k]);
568 }
569 }
570 else {
571 // multiple results in return statement are unexpected
572 throw ChimeraTK::runtime_error("LogicalNameMapping MathPlugin for register '" + varName +
573 "': The expression returned " + std::to_string(results.count()) + " results, expect exactly one result.");
574 }
575 }
576
577} // namespace ChimeraTK::LNMBackend
void remove(AccessMode flag)
Remove the given flag from the set.
Definition AccessMode.cc:56
Class describing the actual payload data format of a register in an abstract manner.
A class to describe which of the supported data types is used.
LNMBackendRegisterInfo _info
RegisterInfo describing the the target register for which this plugin instance should work.
Base class for plugins that modify the behaviour of accessors in the logical name mapping backend.
size_t _pluginIndex
Index of the plugin instance within the stack of plugins on a particular register.
exprtk::rtl::vecops::package< double > vecOpsPkg
boost::shared_ptr< LogicalNameMappingBackend > _backend
boost::shared_ptr< LogicalNameMappingBackend > getBackend()
boost::shared_ptr< NDRegisterAccessor< double > > _target
void setExceptionBackend(boost::shared_ptr< DeviceBackend > exceptionBackend)
void compileFormula(const std::string &formula, const std::map< std::string, boost::shared_ptr< ChimeraTK::NDRegisterAccessor< double > > > &parameters, size_t nElements)
std::map< std::string, boost::shared_ptr< NDRegisterAccessor< double > > > _accessorMap
std::unique_ptr< exprtk::vector_view< double > > valueView
MathPluginFormulaHelper(MathPlugin *p, const boost::shared_ptr< LogicalNameMappingBackend > &backend)
std::map< boost::shared_ptr< NDRegisterAccessor< double > >, std::unique_ptr< exprtk::vector_view< double > > > params
void computeResult(std::vector< double > &x, std::vector< T > &resultBuffer)
void updateResult(ChimeraTK::VersionNumber versionNumber)
Math Plugin: Apply mathematical formula to register's data.
ChimeraTK::DataValidity _lastMainValidity
void exceptionHook() override
Hook called when an exception is reported to the the backend via setException(), after the backend ha...
boost::shared_ptr< NDRegisterAccessor< UserType > > decorateAccessor(boost::shared_ptr< LogicalNameMappingBackend > &backend, boost::shared_ptr< NDRegisterAccessor< TargetType > > &target, const UndecoratedParams &accessorParams)
void openHook(const boost::shared_ptr< LogicalNameMappingBackend > &backend) override
Hook called when the backend is opened, at the end of the open() function after all backend work has ...
static thread_local int64_t _writeLockCounter
MathPlugin(const LNMBackendRegisterInfo &info, size_t pluginIndex, std::map< std::string, std::string > parameters)
LNMBackendRegisterInfo * info()
std::map< std::string, std::string > _parameters
void closeHook() override
Hook called when the backend is closed, at the beginning of the close() function when the device is s...
std::recursive_mutex _writeMutex
boost::shared_ptr< MathPluginFormulaHelper > getFormulaHelper(boost::shared_ptr< LogicalNameMappingBackend > backend)
if not yet existing, creates the instance and returns it if already existing, backend ptr may be empt...
void postParsingHook(const boost::shared_ptr< const LogicalNameMappingBackend > &backend) override
Hook called after the parsing of logical name map.
void doRegisterInfoUpdate() override
Implementation of the plugin specific register information update.
std::vector< double > _lastMainValue
RegisterInfo structure for the LogicalNameMappingBackend.
RegisterPath getRegisterName() const override
Return full path name of the register (including modules)
AccessModeFlags supportedFlags
Supported AccessMode flags.
bool readable
Flag if the register is readable.
bool writeable
Flag if the register is writeable.
unsigned int length
The length of the range (i.e.
bool isWriteable() const override
Return whether the register is writeable.
RegisterPath name
Name of the register.
Base class for decorators of the NDRegisterAccessor.
N-dimensional register accessor.
std::vector< std::vector< UserType > > buffer_2D
Buffer of converted data elements.
Class to store a register path name.
const std::string & getName() const
Returns the name that identifies the process variable.
boost::shared_ptr< DeviceBackend > _exceptionBackend
The backend to which the runtime_errors are reported via DeviceBackend::setException().
Class for generating and holding version numbers without exposing a numeric representation.
Exception thrown when a logic error has occured.
Definition Exception.h:51
Exception thrown when a runtime error has occured.
Definition Exception.h:18
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Definition Exception.cpp:14
DataValidity
The current state of the data.
@ faulty
The data is considered valid.
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
TransferType
Used to indicate the applicable operation on a Transferelement.
STL namespace.
std::string to_string(const std::string &v)
MathPluginDecorator(boost::shared_ptr< LogicalNameMappingBackend > &backend, const boost::shared_ptr< ChimeraTK::NDRegisterAccessor< double > > &target, MathPlugin *p)
bool isReadable() const override
Check if transfer element is readable.
bool doWriteTransfer(ChimeraTK::VersionNumber) override
Implementation version of writeTransfer().
boost::shared_ptr< MathPluginFormulaHelper > _h
bool doWriteTransferDestructively(ChimeraTK::VersionNumber) override
Implementation version of writeTransferDestructively().
void doPreWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of preWrite().
void setExceptionBackend(boost::shared_ptr< DeviceBackend >) override
Set the backend to which the exception has to be reported.
void doPostWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of postWrite().
void doPreRead(TransferType type) override
Backend specific implementation of preRead().
void doPostRead(TransferType type, bool hasNewData) override
Backend specific implementation of postRead().
Helper struct to hold extra parameters needed by some plugins, used in decorateAccessor()