12#include <ChimeraTK/cppext/finally.hpp>
61 const auto& catalogue = backend->_catalogue_mutable;
68 auto r = catalogue.getRegister(parpair.second);
77 std::unique_lock<std::recursive_mutex> lk(
_writeMutex);
88 auto catalogue = backend->getRegisterCatalogue();
95 if(backend->_variables.find(pname) == backend->_variables.end()) {
96 throw logic_error(
"no LNM variable defined for parameter " + pname);
102 backend->_variables[pname].usingFormulas.insert(
this);
106 throw ChimeraTK::logic_error(
"MathPlugin (writing) with push_parameters requested but there is no parameter!");
114 boost::shared_ptr<LogicalNameMappingBackend> backend) {
116 std::unique_lock<std::recursive_mutex> lk{
_writeMutex};
123 p = boost::make_shared<MathPluginFormulaHelper>(
this, backend);
135 p.first->readLatest();
141 return paramDataValidity;
170 auto writeVs = std::max<ChimeraTK::VersionNumber>(versionNumber,
_target->getVersionNumber());
171 _target->writeDestructively(writeVs);
177 _target->getExceptionBackend()->setException(ex.
what());
201 if(acc.second->getVersionNumber() <=
_backend->getVersionOnOpen()) {
211 template<
typename UserType>
220 throw ChimeraTK::logic_error(
"This register with MathPlugin enabled is not readable: " + _target->getName());
222 _target->preRead(type);
237 boost::shared_ptr<MathPluginFormulaHelper>
_h;
251 template<
typename UserType>
256 if(_target->getNumberOfChannels() != 1) {
258 "The LogicalNameMapper MathPlugin supports only scalar or 1D array registers. Register name: " +
264 target->setExceptionBackend(backend);
272 template<
typename UserType>
274 _target->postRead(type, hasNewData);
275 if(!hasNewData)
return;
277 auto paramDataValidity = _h->updateParameters();
280 _h->computeResult(_target->accessChannel(0), buffer_2D[0]);
283 this->_versionNumber = _target->getVersionNumber();
284 this->_dataValidity = _target->dataValidity();
298 template<
typename UserType>
301 throw ChimeraTK::logic_error(
"This register with MathPlugin enabled is not writeable: " + _target->getName());
304 auto backend = _h->getBackend();
305 if(!backend->isOpen()) {
308 backend->checkActiveException();
312 _skipWriteDelegation =
true;
314 auto paramDataValidity = _h->updateParameters();
319 for(
size_t k = 0; k < buffer_2D[0].size(); ++k) {
320 _target->accessData(0, k) = userTypeToNumeric<double>(buffer_2D[0][k]);
324 if(_p->_hasPushParameter) {
328 _p->_writeMutex.lock();
332 ++(_p->_writeLockCounter);
333 _p->_lastMainValue = _target->accessChannel(0);
334 _p->_lastMainValidity = _target->dataValidity();
335 _p->_mainValueWrittenAfterOpen =
true;
337 if(!_h->checkAllParametersWritten()) {
344 _skipWriteDelegation =
false;
347 _h->computeResult(_target->accessChannel(0), _target->accessChannel(0));
357 auto writeVs = std::max<ChimeraTK::VersionNumber>(versionNumber, _target->getVersionNumber());
358 _target->preWrite(type, writeVs);
363 template<
typename UserType>
365 if(_skipWriteDelegation) {
369 return _target->writeTransfer(versionNumber);
374 template<
typename UserType>
376 return doWriteTransfer(versionNumber);
381 template<
typename UserType>
385 auto _ = cppext::finally([&] {
386 if(_p->_writeLockCounter > 0) {
387 --(_p->_writeLockCounter);
388 _p->_writeMutex.unlock();
392 if(_skipWriteDelegation && (this->_activeException !=
nullptr)) {
395 std::rethrow_exception(this->_activeException);
398 if(_skipWriteDelegation) {
403 _target->setActiveException(this->_activeException);
404 _target->postWrite(type, versionNumber);
410 template<
typename UserType>
412 this->_exceptionBackend = exceptionBackend;
413 _target->setExceptionBackend(exceptionBackend);
414 _h->setExceptionBackend(exceptionBackend);
421 p.first->setExceptionBackend(exceptionBackend);
427 template<
typename UserType,
typename TargetType>
431 if constexpr(std::is_same_v<TargetType, double>) {
432 return boost::make_shared<MathPluginDecorator<UserType>>(backend, target,
this);
444 MathPlugin* p,
const boost::shared_ptr<LogicalNameMappingBackend>& backend)
445 : _backend(backend), _mp(p) {
447 auto length = info->
length;
449 _target = backend->getRegisterAccessor_impl<
double>(info->getRegisterName(), 0, 0, {},
_mp->
_pluginIndex + 1);
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 +
"'");
479 exprtk::parser<double> parser;
493 std::vector<double> temp(nElements);
494 valueView = std::make_unique<exprtk::vector_view<double>>(exprtk::make_vector_view(temp, nElements));
498 for(
const auto& parpair : parameters) {
499 if(parpair.first ==
"formula")
continue;
500 const auto& acc = parpair.second;
502 if(acc->getNumberOfChannels() != 1) {
504 "The LogicalNameMapper MathPlugin supports only scalar or 1D array registers. Register name: '" +
varName +
505 "', parameter name: '" + parpair.first +
"'");
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);
515 bool success = parser.compile(formula,
expression);
518 "': failed to compile expression '" + formula +
"': " + parser.error());
531 p.second->rebase(p.first->accessChannel(0).data());
535 double valueWhenNotUsingReturn =
expression.value();
536 exprtk::results_context<double> results =
expression.results();
539 if(results.count() == 0) {
541 if(resultBuffer.size() != 1) {
543 "': The expression returns a scalar but " +
std::to_string(resultBuffer.size()) +
" expected.");
545 resultBuffer[0] = numericToUserType<T>(valueWhenNotUsingReturn);
547 else if(results.count() == 1) {
549 exprtk::type_store<double> result = results[0];
552 if(result.type != exprtk::type_store<double>::e_scalar && result.type != exprtk::type_store<double>::e_vector) {
554 "': The expression did not return a numeric result.");
558 exprtk::type_store<double>::type_view<
double> view(result);
559 if(view.size() != resultBuffer.size()) {
561 "': The expression returns " +
std::to_string(view.size()) +
" elements but " +
566 for(
size_t k = 0; k < view.size(); ++k) {
567 resultBuffer[k] = numericToUserType<T>(view[k]);
573 "': The expression returned " +
std::to_string(results.count()) +
" results, expect exactly one result.");
void remove(AccessMode flag)
Remove the given flag from the set.
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.
Math Plugin: Apply mathematical formula to register's data.
ChimeraTK::DataValidity _lastMainValidity
bool _creatingFormulaHelper
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 ...
bool _enablePushParameters
static thread_local int64_t _writeLockCounter
bool _mainValueWrittenAfterOpen
MathPlugin(const LNMBackendRegisterInfo &info, size_t pluginIndex, std::map< std::string, std::string > parameters)
LNMBackendRegisterInfo * info()
std::map< std::string, std::string > _parameters
bool _allParametersWrittenAfterOpen
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)
DataDescriptor _dataDescriptor
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.
Exception thrown when a runtime error has occured.
const char * what() const noexcept override
Return the message describing what exactly went wrong.
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.
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().
bool _skipWriteDelegation
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()