ChimeraTK-DeviceAccess  03.18.00
LNMBitRangeAccessPlugin.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 "FixedPointConverter.h"
5 #include "LNMAccessorPlugin.h"
7 #include "NDRegisterAccessor.h"
9 
10 #include <boost/make_shared.hpp>
11 
12 #include <charconv>
13 
14 namespace ChimeraTK::LNMBackend {
15 
17  ReferenceCountedUniqueLock() = default;
18 
19  ReferenceCountedUniqueLock(std::recursive_mutex& mutex, int* useCounter)
20  : _lock(mutex, std::defer_lock), _targetUseCount(useCounter) {}
21 
22  void lock() {
23  _lock.lock();
25  }
26 
27  void unlock() {
29  _lock.unlock();
30  }
31 
32  [[nodiscard]] int useCount() const {
33  assert(_lock.owns_lock());
34  return *_targetUseCount;
35  }
36 
37  std::unique_lock<std::recursive_mutex> _lock;
38  int* _targetUseCount{nullptr};
39  };
40 
41  /********************************************************************************************************************/
42 
43  // From https://stackoverflow.com/questions/1392059/algorithm-to-generate-bit-mask
44  constexpr uint64_t getMaskForNBits(uint64_t numberOfBits) {
45  // Prevent warning about undefined behavior if shifting right by 64 bit below
46  if(numberOfBits == 0) {
47  return 0;
48  }
49 
50  return (static_cast<uint64_t>(-(numberOfBits != 0)) &
51  (static_cast<uint64_t>(-1) >> ((sizeof(uint64_t) * CHAR_BIT) - numberOfBits)));
52  }
53 
54  /********************************************************************************************************************/
55 
56  template<typename UserType, typename TargetType>
59 
60  /******************************************************************************************************************/
61  BitRangeAccessPluginDecorator(boost::shared_ptr<LogicalNameMappingBackend>& backend,
62  const boost::shared_ptr<ChimeraTK::NDRegisterAccessor<TargetType>>& target, const std::string& name,
63  uint64_t shift, uint64_t numberOfBits, uint64_t dataInterpretationFractionalBits,
64  uint64_t dataInterpretationIsSigned)
65  : ChimeraTK::NDRegisterAccessorDecorator<UserType, TargetType>(target), _shift(shift),
66  _numberOfBits(numberOfBits), _writeable{_target->isWriteable()},
67  fixedPointConverter(name, _numberOfBits, dataInterpretationFractionalBits, dataInterpretationIsSigned) {
68  if(_target->getNumberOfChannels() > 1 || _target->getNumberOfSamples() > 1) {
69  throw ChimeraTK::logic_error("LogicalNameMappingBackend BitRangeAccessPluginDecorator: " +
70  TransferElement::getName() + ": Cannot target non-scalar registers.");
71  }
72 
73  auto& map = boost::fusion::at_key<TargetType>(backend->sharedAccessorMap.table);
74  RegisterPath path{name};
75  path.setAltSeparator(".");
76  LogicalNameMappingBackend::AccessorKey key{backend.get(), path};
77 
78  auto it = map.find(key);
79  if(it != map.end()) {
80  _lock = {it->second.mutex, &(it->second.useCount)};
81  }
82  else {
83  assert(false);
84  }
85 
88  }
89 
90  /******************************************************************************************************************/
91 
92  void doPreRead(TransferType type) override {
93  _lock.lock();
94 
95  _target->preRead(type);
96  }
97 
98  /******************************************************************************************************************/
99 
100  void doPostRead(TransferType type, bool hasNewData) override {
101  auto unlock = cppext::finally([this] { this->_lock.unlock(); });
102  _target->postRead(type, hasNewData);
103  if(!hasNewData) return;
104 
105  if constexpr(std::is_same_v<uint64_t, TargetType>) {
106  auto validity = _target->dataValidity();
107  uint64_t v{_target->accessData(0)};
108  v = (v & _maskOnTarget) >> _shift;
109 
110  buffer_2D[0][0] = fixedPointConverter.scalarToCooked<UserType>(uint32_t(v));
111  // Do a quick check if the fixed point converter clamped. Then set the
112  // data validity faulty according to B.2.4.1
113  // For proper implementation of this, the fixed point converter needs to signalize
114  // that it had clamped. See https://redmine.msktools.desy.de/issues/12912
115  auto raw = fixedPointConverter.toRaw(buffer_2D[0][0]);
116  if(raw != v) {
117  validity = DataValidity::faulty;
118  }
119 
120  this->_versionNumber = std::max(this->_versionNumber, _target->getVersionNumber());
121  this->_dataValidity = validity;
122  }
123  else {
124  // This code should never be reached so long the the bit range plugin requires its target type to be uint64
125  assert(false);
126  }
127  }
128 
129  /******************************************************************************************************************/
130 
131  void doPreWrite(TransferType type, VersionNumber versionNumber) override {
132  _lock.lock();
133 
134  if(!_writeable) {
136  "Register \"" + TransferElement::getName() + "\" with BitRange plugin is not writeable.");
137  }
138 
139  auto value = fixedPointConverter.toRaw(buffer_2D[0][0]);
140 
141  // FIXME: Not setting the data validity according to the spec point B2.5.1.
142  // This needs a change in the fixedpoint converter to tell us that it has clamped the value to reliably work.
143  // To be revisted after fixing https://redmine.msktools.desy.de/issues/12912
144 
145  // When in a transfer group, only the first accessor to write to the _target can call read() in its preWrite()
146  // Otherwise it will overwrite the
147  if(_target->isReadable() && (!TransferElement::_isInTransferGroup || _lock.useCount() == 1)) {
148  _target->read();
149  }
150 
151  _target->accessData(0) &= ~_maskOnTarget;
152  _target->accessData(0) |= (value << _shift);
153 
154  _temporaryVersion = std::max(versionNumber, _target->getVersionNumber());
155  _target->setDataValidity(this->_dataValidity);
156  _target->preWrite(type, _temporaryVersion);
157  }
158 
159  /******************************************************************************************************************/
160 
161  void doPostWrite(TransferType type, VersionNumber /*versionNumber*/) override {
162  auto unlock = cppext::finally([this] { this->_lock.unlock(); });
163  _target->postWrite(type, _temporaryVersion);
164  }
165 
166  /******************************************************************************************************************/
167 
168  void replaceTransferElement(boost::shared_ptr<ChimeraTK::TransferElement> newElement) override {
169  auto casted = boost::dynamic_pointer_cast<BitRangeAccessPluginDecorator<UserType, TargetType>>(newElement);
170 
171  // In a transfer group, we are trying to replaced with an accessor. Check if this accessor is for the
172  // same target and not us and check for overlapping bit range afterwards. If they overlap, switch us and
173  // the replacement read-only which switches the transfergroup read-only since we cannot guarantee the write order
174  // for overlapping bit ranges
175  if(casted && casted.get() != this && casted->_target == _target) {
176  if((casted->_maskOnTarget & _maskOnTarget) != 0) {
177  casted->_writeable = false;
178  _writeable = false;
179  }
180  }
182  }
183 
184  /******************************************************************************************************************/
185 
186  uint64_t _shift;
187  uint64_t _numberOfBits;
188  uint64_t _maskOnTarget;
189  uint64_t _userTypeMask{getMaskForNBits(sizeof(UserType) * CHAR_BIT)};
190  uint64_t _targetTypeMask{getMaskForNBits(sizeof(TargetType) * CHAR_BIT)};
191  uint64_t _baseBitMask;
192 
195  bool _writeable{false};
197 
199  };
200 
201  /********************************************************************************************************************/
202 
204  const LNMBackendRegisterInfo& info, size_t pluginIndex, const std::map<std::string, std::string>& parameters)
205  : AccessorPlugin<BitRangeAccessPlugin>(info, pluginIndex, true) {
206  try {
207  const auto& shift = parameters.at("shift");
208 
209  // This is how you are supposed to use std::from_chars with std::string
210  // NOLINTNEXTLINE(unsafe-buffer-usage)
211  auto [suffix, ec]{std::from_chars(shift.data(), shift.data() + shift.size(), _shift)};
212  if(ec != std::errc()) {
213  throw ChimeraTK::logic_error("LogicalNameMappingBackend BitRangeAccessPlugin: " + info.getRegisterName() +
214  R"(: Unparseable parameter "shift".)");
215  }
216  }
217  catch(std::out_of_range&) {
218  throw ChimeraTK::logic_error("LogicalNameMappingBackend BitRangeAccessPlugin: " + info.getRegisterName() +
219  R"(: Missing parameter "shift".)");
220  }
221 
222  try {
223  const auto& numberOfBits = parameters.at("numberOfBits");
224  // This is how you are supposed to use std::from_chars with std::string
225  // NOLINTNEXTLINE(unsafe-buffer-usage)
226  auto [suffix, ec]{std::from_chars(numberOfBits.data(), numberOfBits.data() + numberOfBits.size(), _numberOfBits)};
227  if(ec != std::errc()) {
228  throw ChimeraTK::logic_error("LogicalNameMappingBackend BitRangeAccessPlugin: " + info.getRegisterName() +
229  R"(: Unparseable parameter "numberOfBits".)");
230  }
231  }
232  catch(std::out_of_range&) {
233  throw ChimeraTK::logic_error("LogicalNameMappingBackend BitRangeAccessPlugin: " + info.getRegisterName() +
234  R"(: Unparseable parameter "numberOfBits".)");
235  }
236 
237  if(const auto it = parameters.find("fractionalBits"); it != parameters.end()) {
238  // This is how you are supposed to use std::from_chars with std::string
239  // NOLINTNEXTLINE(unsafe-buffer-usage)
240  auto [suffix, ec]{
241  std::from_chars(it->second.data(), it->second.data() + it->second.size(), dataInterpretationFractionalBits)};
242  if(ec != std::errc()) {
243  throw ChimeraTK::logic_error("LogicalNameMappingBackend BitRangeAccessPlugin: " + info.getRegisterName() +
244  R"(: Unparseable parameter "fractionalBits".)");
245  }
246  }
247 
248  if(const auto it = parameters.find("signed"); it != parameters.end()) {
249  std::stringstream ss(it->second);
250  Boolean value;
251  ss >> value;
253  }
254  }
255 
256  /********************************************************************************************************************/
257 
259  // We do not support wait_for_new_data with this decorator
262  // also remove raw-type info from DataDescriptor
264  }
265 
266  /********************************************************************************************************************/
267 
268  template<typename UserType, typename TargetType>
269  boost::shared_ptr<NDRegisterAccessor<UserType>> BitRangeAccessPlugin::decorateAccessor(
270  boost::shared_ptr<LogicalNameMappingBackend>& backend, boost::shared_ptr<NDRegisterAccessor<TargetType>>& target,
271  const UndecoratedParams& params) {
272  if constexpr(std::is_integral<TargetType>::value) {
273  return boost::make_shared<BitRangeAccessPluginDecorator<UserType, TargetType>>(backend, target, params._name,
275  }
276 
277  assert(false);
278 
279  return {};
280  }
281 
282 } // namespace ChimeraTK::LNMBackend
ChimeraTK::LogicalNameMappingBackend::AccessorKey
std::pair< DeviceBackend *, RegisterPath > AccessorKey
Map of target accessors which are potentially shared across our accessors.
Definition: LogicalNameMappingBackend.h:82
ChimeraTK::LNMBackend::BitRangeAccessPlugin::decorateAccessor
boost::shared_ptr< NDRegisterAccessor< UserType > > decorateAccessor(boost::shared_ptr< LogicalNameMappingBackend > &backend, boost::shared_ptr< NDRegisterAccessor< TargetType >> &target, const UndecoratedParams &accessorParams)
Definition: LNMBitRangeAccessPlugin.cc:269
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::replaceTransferElement
void replaceTransferElement(boost::shared_ptr< ChimeraTK::TransferElement > newElement) override
Definition: LNMBitRangeAccessPlugin.cc:168
ChimeraTK::AccessMode::raw
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
ChimeraTK::LNMBackend::UndecoratedParams::_name
std::string _name
Definition: LNMAccessorPlugin.h:17
ChimeraTK::LNMBackend::ReferenceCountedUniqueLock
Definition: LNMBitRangeAccessPlugin.cc:16
ChimeraTK::NDRegisterAccessorDecorator::replaceTransferElement
void replaceTransferElement(boost::shared_ptr< ChimeraTK::TransferElement > newElement) override
Definition: NDRegisterAccessorDecorator.h:203
ChimeraTK::LNMBackendRegisterInfo::getRegisterName
RegisterPath getRegisterName() const override
Return full path name of the register (including modules)
Definition: LNMBackendRegisterInfo.h:32
ChimeraTK::LNMBackend::BitRangeAccessPlugin
Definition: LNMAccessorPlugin.h:246
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::doPreRead
void doPreRead(TransferType type) override
Definition: LNMBitRangeAccessPlugin.cc:92
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::fixedPointConverter
FixedPointConverter fixedPointConverter
Definition: LNMBitRangeAccessPlugin.cc:196
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_numberOfBits
uint64_t _numberOfBits
Definition: LNMBitRangeAccessPlugin.cc:187
ChimeraTK::DataValidity::faulty
@ faulty
The data is considered valid.
ChimeraTK::TransferElement::getName
const std::string & getName() const
Returns the name that identifies the process variable.
Definition: TransferElement.h:88
ChimeraTK::LNMBackend::AccessorPlugin
Base class for plugins that modify the behaviour of accessors in the logical name mapping backend.
Definition: LNMAccessorPlugin.h:104
ChimeraTK::LNMBackend::BitRangeAccessPlugin::BitRangeAccessPlugin
BitRangeAccessPlugin(const LNMBackendRegisterInfo &info, size_t pluginIndex, const std::map< std::string, std::string > &parameters)
Definition: LNMBitRangeAccessPlugin.cc:203
FixedPointConverter.h
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_writeable
bool _writeable
Definition: LNMBitRangeAccessPlugin.cc:195
ChimeraTK::LNMBackend::ReferenceCountedUniqueLock::ReferenceCountedUniqueLock
ReferenceCountedUniqueLock(std::recursive_mutex &mutex, int *useCounter)
Definition: LNMBitRangeAccessPlugin.cc:19
ChimeraTK::FixedPointConverter::scalarToCooked
UserType scalarToCooked(int32_t const &raw) const
Inefficient convenience function for converting a single value to cooked.
Definition: FixedPointConverter.h:86
ChimeraTK::LNMBackend
Definition: LNMMathPluginFormulaHelper.h:16
ChimeraTK::DataDescriptor::setRawDataType
void setRawDataType(const DataType &d)
Set the raw data type.
Definition: DataDescriptor.cpp:116
ChimeraTK::LNMBackend::ReferenceCountedUniqueLock::lock
void lock()
Definition: LNMBitRangeAccessPlugin.cc:22
ChimeraTK::LNMBackend::BitRangeAccessPlugin::dataInterpretationFractionalBits
uint32_t dataInterpretationFractionalBits
Definition: LNMAccessorPlugin.h:261
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_targetTypeMask
uint64_t _targetTypeMask
Definition: LNMBitRangeAccessPlugin.cc:190
ChimeraTK::LNMBackend::BitRangeAccessPlugin::_shift
uint32_t _shift
Definition: LNMAccessorPlugin.h:258
ChimeraTK::TransferElement::_isInTransferGroup
bool _isInTransferGroup
Flag whether this TransferElement has been added to a TransferGroup or not.
Definition: TransferElement.h:820
ChimeraTK::AccessModeFlags::remove
void remove(AccessMode flag)
Remove the given flag from the set.
Definition: AccessMode.cc:56
ChimeraTK::LNMBackend::getMaskForNBits
constexpr uint64_t getMaskForNBits(uint64_t numberOfBits)
Definition: LNMBitRangeAccessPlugin.cc:44
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::doPostRead
void doPostRead(TransferType type, bool hasNewData) override
Definition: LNMBitRangeAccessPlugin.cc:100
ChimeraTK::NDRegisterAccessor::buffer_2D
std::vector< std::vector< UserType > > buffer_2D
Buffer of converted data elements.
Definition: NDRegisterAccessor.h:123
ChimeraTK::RegisterPath::setAltSeparator
void setAltSeparator(const std::string &altSeparator)
set alternative separator.
Definition: RegisterPath.h:37
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::doPostWrite
void doPostWrite(TransferType type, VersionNumber) override
Definition: LNMBitRangeAccessPlugin.cc:161
NDRegisterAccessorDecorator.h
ChimeraTK::LNMBackend::UndecoratedParams
Helper struct to hold extra parameters needed by some plugins, used in decorateAccessor()
Definition: LNMAccessorPlugin.h:13
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator
Definition: LNMBitRangeAccessPlugin.cc:57
ChimeraTK::LNMBackend::ReferenceCountedUniqueLock::useCount
int useCount() const
Definition: LNMBitRangeAccessPlugin.cc:32
ChimeraTK::LNMBackend::ReferenceCountedUniqueLock::ReferenceCountedUniqueLock
ReferenceCountedUniqueLock()=default
NDRegisterAccessor.h
ChimeraTK::AccessMode::wait_for_new_data
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
ChimeraTK::LNMBackendRegisterInfo
RegisterInfo structure for the LogicalNameMappingBackend.
Definition: LNMBackendRegisterInfo.h:22
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_lock
ReferenceCountedUniqueLock _lock
Definition: LNMBitRangeAccessPlugin.cc:193
ChimeraTK::DataType::none
@ none
The data type/concept does not exist, e.g. there is no raw transfer (do not confuse with Void)
Definition: SupportedUserTypes.h:606
ChimeraTK::LNMBackend::BitRangeAccessPlugin::doRegisterInfoUpdate
void doRegisterInfoUpdate() override
Implementation of the plugin specific register information update.
Definition: LNMBitRangeAccessPlugin.cc:258
ChimeraTK::LNMBackend::AccessorPluginBase::_info
LNMBackendRegisterInfo _info
RegisterInfo describing the the target register for which this plugin instance should work.
Definition: LNMAccessorPlugin.h:93
ChimeraTK::TransferType
TransferType
Used to indicate the applicable operation on a Transferelement.
Definition: TransferElement.h:51
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_temporaryVersion
VersionNumber _temporaryVersion
Definition: LNMBitRangeAccessPlugin.cc:194
ChimeraTK::LNMBackend::BitRangeAccessPlugin::_numberOfBits
uint32_t _numberOfBits
Definition: LNMAccessorPlugin.h:259
ChimeraTK::NDRegisterAccessorDecorator
Base class for decorators of the NDRegisterAccessor.
Definition: NDRegisterAccessorDecorator.h:120
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_baseBitMask
uint64_t _baseBitMask
Definition: LNMBitRangeAccessPlugin.cc:191
ChimeraTK::LNMBackendRegisterInfo::_dataDescriptor
DataDescriptor _dataDescriptor
Definition: LNMBackendRegisterInfo.h:111
ChimeraTK::LNMBackend::BitRangeAccessPlugin::dataInterpretationIsSigned
bool dataInterpretationIsSigned
Definition: LNMAccessorPlugin.h:262
ChimeraTK::LNMBackend::ReferenceCountedUniqueLock::unlock
void unlock()
Definition: LNMBitRangeAccessPlugin.cc:27
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_shift
uint64_t _shift
Definition: LNMBitRangeAccessPlugin.cc:186
ChimeraTK::RegisterPath
Class to store a register path name.
Definition: RegisterPath.h:16
ChimeraTK::LNMBackendRegisterInfo::supportedFlags
AccessModeFlags supportedFlags
Supported AccessMode flags.
Definition: LNMBackendRegisterInfo.h:106
ChimeraTK::VersionNumber
Class for generating and holding version numbers without exposing a numeric representation.
Definition: VersionNumber.h:23
ChimeraTK::FixedPointConverter
The fixed point converter provides conversion functions between a user type and up to 32 bit fixed po...
Definition: FixedPointConverter.h:28
LNMBackendRegisterInfo.h
ChimeraTK::FixedPointConverter::toRaw
uint32_t toRaw(UserType cookedValue) const
Conversion function from type T to fixed point.
Definition: FixedPointConverter.h:336
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_userTypeMask
uint64_t _userTypeMask
Definition: LNMBitRangeAccessPlugin.cc:189
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::doPreWrite
void doPreWrite(TransferType type, VersionNumber versionNumber) override
Definition: LNMBitRangeAccessPlugin.cc:131
ChimeraTK::LNMBackend::ReferenceCountedUniqueLock::_lock
std::unique_lock< std::recursive_mutex > _lock
Definition: LNMBitRangeAccessPlugin.cc:37
ChimeraTK::Boolean
Wrapper Class to avoid vector<bool> problems.
Definition: SupportedUserTypes.h:21
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::BitRangeAccessPluginDecorator
BitRangeAccessPluginDecorator(boost::shared_ptr< LogicalNameMappingBackend > &backend, const boost::shared_ptr< ChimeraTK::NDRegisterAccessor< TargetType >> &target, const std::string &name, uint64_t shift, uint64_t numberOfBits, uint64_t dataInterpretationFractionalBits, uint64_t dataInterpretationIsSigned)
Definition: LNMBitRangeAccessPlugin.cc:61
ChimeraTK::LNMBackend::BitRangeAccessPluginDecorator::_maskOnTarget
uint64_t _maskOnTarget
Definition: LNMBitRangeAccessPlugin.cc:188
ChimeraTK::LNMBackend::ReferenceCountedUniqueLock::_targetUseCount
int * _targetUseCount
Definition: LNMBitRangeAccessPlugin.cc:38
ChimeraTK::NDRegisterAccessor
N-dimensional register accessor.
Definition: ForwardDeclarations.h:17
LNMAccessorPlugin.h
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51