12 #include <ChimeraTK/cppext/finally.hpp>
22 :
AccessorPlugin(info, pluginIndex), _parameters(std::move(parameters)) {
61 auto catalogue = backend->getRegisterCatalogue();
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);
265 this->_exceptionBackend = 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.");