ChimeraTK-DeviceAccess  03.18.00
LNMDoubleBufferPlugin.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 
6 #include "BackendFactory.h"
7 
8 namespace ChimeraTK::LNMBackend {
10  const LNMBackendRegisterInfo& info, size_t pluginIndex, std::map<std::string, std::string> parameters)
11  : AccessorPlugin(info, pluginIndex), _parameters(std::move(parameters)) {
13  // we do not support redirectedChannel with doubleBuffer because it has no benefit and will cause
14  // trouble in interplay with TransferGroup
15  throw logic_error(
16  "doubleBuffer plugin not supported for redirectedChannel! use it with redirectedRegister instead");
17  }
18  _targetDeviceName = info.deviceName;
19  }
20 
22  // Change register info to read-only
23  _info.writeable = false;
25  // also remove raw-type info from DataDescriptor
27  }
28 
29  template<typename UserType, typename TargetType>
30  boost::shared_ptr<NDRegisterAccessor<UserType>> DoubleBufferPlugin::decorateAccessor(
31  boost::shared_ptr<LogicalNameMappingBackend>& backend, boost::shared_ptr<NDRegisterAccessor<TargetType>>& target,
32  const UndecoratedParams& accessorParams) {
33  if constexpr(std::is_same<UserType, TargetType>::value) {
34  return boost::make_shared<DoubleBufferAccessorDecorator<UserType>>(backend, target, *this, accessorParams);
35  }
36  assert(false);
37  return {};
38  }
39 
40  template<typename UserType>
42  boost::shared_ptr<LogicalNameMappingBackend>& backend, boost::shared_ptr<NDRegisterAccessor<UserType>>& target,
43  DoubleBufferPlugin& plugin, const UndecoratedParams& accessorParams)
44  : ChimeraTK::NDRegisterAccessorDecorator<UserType>(target), _plugin(plugin) {
45  boost::shared_ptr<DeviceBackend> dev;
46  const auto& parameters = plugin._parameters;
47  try {
48  if(plugin._targetDeviceName != "this") {
49  dev = backend->_devices.at(plugin._targetDeviceName);
50  }
51  else {
52  dev = backend;
53  }
54  }
55  catch(std::out_of_range& ex) {
56  std::string message = "LogicalNameMappingBackend DoubleBufferPlugin: unknown targetDevice " + std::string("'") +
57  plugin._targetDeviceName + "'.";
58  throw ChimeraTK::logic_error(message);
59  }
60 
61  size_t daqNumber = 0;
62  // parameter daqNumber is optional, defines the array index offset in the control & status registers
63  if(parameters.find("daqNumber") != parameters.end()) {
64  try {
65  daqNumber = std::stoul(parameters.at("daqNumber"));
66  }
67  catch(std::exception& e) {
69  "LogicalNameMappingBackend DoubleBufferPlugin: parameter 'daqNumber' must be integer");
70  }
71  }
72  // FIXME - remove testUSleep feature
73  if(parameters.find("testUSleep") != parameters.end()) {
74  try {
75  _testUSleep = std::stoul(parameters.at("testUSleep"));
76  }
77  catch(std::exception& e) {
79  "LogicalNameMappingBackend DoubleBufferPlugin: parameter 'testUSleep' must be integer");
80  }
81  }
82 
83  std::string key; // store key searched in 'parameters' map in order to print later correctly exception message
84  try {
85  _enableDoubleBufferReg =
86  dev->getRegisterAccessor<uint32_t>(parameters.at(key.assign("enableDoubleBuffering")), 1, daqNumber, {});
87  _currentBufferNumberReg =
88  dev->getRegisterAccessor<uint32_t>(parameters.at(key.assign("currentBufferNumber")), 1, daqNumber, {});
89  std::string secondBufName = parameters.at(key.assign("secondBuffer"));
90 
91  // take over the offset/numWords of this logical register, also for second buffer
92  // we need to combine it with user-requested offset and lengths, of getRegisterAccessor()
93  size_t offset = size_t(_plugin._info.firstIndex) + accessorParams._wordOffsetInRegister;
94  size_t numWords =
95  (accessorParams._numberOfWords > 0) ? accessorParams._numberOfWords : size_t(_plugin._info.length);
96  auto flags = accessorParams._flags;
97  _secondBufferReg = dev->getRegisterAccessor<UserType>(secondBufName, numWords, offset, flags);
98  }
99  catch(std::out_of_range& ex) {
100  std::string message =
101  "LogicalNameMappingBackend DoubleBufferPlugin: Missing parameter " + std::string("'") + key + "'.";
102  throw ChimeraTK::logic_error(message);
103  }
104  if(_secondBufferReg->getNumberOfChannels() != _target->getNumberOfChannels()) {
105  throw ChimeraTK::logic_error("LogicalNameMappingBackend DoubleBufferPlugin: shapes of first and second buffer do "
106  "not match, different number of channels");
107  }
108  if(_secondBufferReg->getNumberOfSamples() != _target->getNumberOfSamples()) {
109  throw ChimeraTK::logic_error("LogicalNameMappingBackend DoubleBufferPlugin: shapes of first and second buffer do "
110  "not match, different number of samples");
111  }
112  }
113 
114  template<typename UserType>
116  {
117  std::lock_guard lg{_plugin._readerCount.mutex};
118  _plugin._readerCount.value++;
119 
120  // acquire a lock in firmware (disable buffer swapping)
121  _enableDoubleBufferReg->accessData(0) = 0;
122  _enableDoubleBufferReg->write();
123  }
124  if(_testUSleep) {
125  // for testing, extra sleep
126  // FIXME - remove testUSleep feature
127  boost::this_thread::sleep_for(boost::chrono::microseconds{_testUSleep});
128  }
129 
130  // check which buffer is now in use by the firmware
131  _currentBufferNumberReg->read();
132  _currentBuffer = _currentBufferNumberReg->accessData(0);
133  // if current buffer 1, it means firmware writes now to buffer1, so use target (buffer 0), else use
134  // _secondBufferReg (buffer 1)
135  if(_currentBuffer) {
136  _target->preRead(type);
137  }
138  else {
139  _secondBufferReg->preRead(type);
140  }
141  }
142 
143  template<typename UserType>
145  if(_currentBuffer) {
146  _target->readTransfer();
147  }
148  else {
149  _secondBufferReg->readTransfer();
150  }
151  }
152 
153  template<typename UserType>
155  if(_currentBuffer) {
156  _target->postRead(type, hasNewData);
157  }
158  else {
159  _secondBufferReg->postRead(type, hasNewData);
160  }
161 
162  {
163  std::lock_guard lg{_plugin._readerCount.mutex};
164  assert(_plugin._readerCount.value > 0);
165  _plugin._readerCount.value--;
166  if(_plugin._readerCount.value == 0) {
167  if(_testUSleep) {
168  // for testing, check safety of handshake
169  // FIXME - remove testUSleep feature
170  _currentBufferNumberReg->read();
171  if(_currentBuffer != _currentBufferNumberReg->accessData(0)) {
172  std::cout << "WARNING: buffer switch happened while reading! Expect corrupted data." << std::endl;
173  }
174  }
175  // release a lock in firmware (enable buffer swapping)
176  _enableDoubleBufferReg->accessData(0) = 1;
177  _enableDoubleBufferReg->write();
178  }
179  }
180  // set version and data validity of this object
181  this->_versionNumber = {};
182  if(_currentBuffer) {
183  this->_dataValidity = _target->dataValidity();
184  }
185  else {
186  this->_dataValidity = _secondBufferReg->dataValidity();
187  }
188 
189  if(!hasNewData) return;
190 
191  if(_currentBuffer) {
192  for(size_t i = 0; i < _target->getNumberOfChannels(); ++i) {
193  ChimeraTK::NDRegisterAccessorDecorator<UserType>::buffer_2D[i].swap(_target->accessChannel(i));
194  }
195  }
196  else {
197  for(size_t i = 0; i < _secondBufferReg->getNumberOfChannels(); ++i) {
198  ChimeraTK::NDRegisterAccessorDecorator<UserType>::buffer_2D[i].swap(_secondBufferReg->accessChannel(i));
199  }
200  }
201  }
202 
203  template<typename UserType>
204  std::vector<boost::shared_ptr<TransferElement>> DoubleBufferAccessorDecorator<
205  UserType>::getHardwareAccessingElements() {
206  // returning only this means the DoubleBufferAccessorDecorator will not be optimized when put into TransferGroup
207  // optimizing would break our handshake protocol, since it reorders transfers
208  return {TransferElement::shared_from_this()};
209  }
210 
211  template<typename UserType>
213  const boost::shared_ptr<const TransferElement>& other) const {
214  // we need this to support merging of accessors using the same double-buffered as target.
215  // If other is also double-buffered region belonging to the same plugin instance, allow the merge
216  auto otherDoubleBuffer = boost::dynamic_pointer_cast<DoubleBufferAccessorDecorator const>(other);
217  if(!otherDoubleBuffer) {
218  return false;
219  }
220  return &(otherDoubleBuffer->_plugin) == &_plugin;
221  }
222 } // namespace ChimeraTK::LNMBackend
ChimeraTK::LNMBackend::UndecoratedParams::_flags
AccessModeFlags _flags
Definition: LNMAccessorPlugin.h:20
ChimeraTK::AccessMode::raw
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
ChimeraTK::LNMBackendRegisterInfo::CHANNEL
@ CHANNEL
Definition: LNMBackendRegisterInfo.h:25
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
Definition: LNMMathPluginFormulaHelper.h:16
ChimeraTK::DataDescriptor::setRawDataType
void setRawDataType(const DataType &d)
Set the raw data type.
Definition: DataDescriptor.cpp:116
ChimeraTK::LNMBackend::UndecoratedParams::_wordOffsetInRegister
size_t _wordOffsetInRegister
Definition: LNMAccessorPlugin.h:19
ChimeraTK::AccessModeFlags::remove
void remove(AccessMode flag)
Remove the given flag from the set.
Definition: AccessMode.cc:56
ChimeraTK::LNMBackend::DoubleBufferPlugin::decorateAccessor
boost::shared_ptr< NDRegisterAccessor< UserType > > decorateAccessor(boost::shared_ptr< LogicalNameMappingBackend > &backend, boost::shared_ptr< NDRegisterAccessor< TargetType >> &target, const UndecoratedParams &accessorParams)
Definition: LNMDoubleBufferPlugin.cc:30
ChimeraTK::LNMBackend::DoubleBufferPlugin::DoubleBufferPlugin
DoubleBufferPlugin(const LNMBackendRegisterInfo &info, size_t pluginIndex, std::map< std::string, std::string > parameters)
Definition: LNMDoubleBufferPlugin.cc:9
ChimeraTK::LNMBackend::UndecoratedParams
Helper struct to hold extra parameters needed by some plugins, used in decorateAccessor()
Definition: LNMAccessorPlugin.h:13
ChimeraTK::LNMBackendRegisterInfo::writeable
bool writeable
Flag if the register is writeable.
Definition: LNMBackendRegisterInfo.h:103
ChimeraTK::LNMBackendRegisterInfo::firstIndex
unsigned int firstIndex
The first index in the range.
Definition: LNMBackendRegisterInfo.h:59
ChimeraTK::LNMBackend::DoubleBufferAccessorDecorator::DoubleBufferAccessorDecorator
DoubleBufferAccessorDecorator(boost::shared_ptr< LogicalNameMappingBackend > &backend, boost::shared_ptr< NDRegisterAccessor< UserType >> &target, DoubleBufferPlugin &plugin, const UndecoratedParams &accessorParams)
Definition: LNMDoubleBufferPlugin.cc:41
ChimeraTK::LNMBackendRegisterInfo
RegisterInfo structure for the LogicalNameMappingBackend.
Definition: LNMBackendRegisterInfo.h:22
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::AccessorPluginBase::_info
LNMBackendRegisterInfo _info
RegisterInfo describing the the target register for which this plugin instance should work.
Definition: LNMAccessorPlugin.h:93
ChimeraTK::LNMBackendRegisterInfo::deviceName
std::string deviceName
The target device alias.
Definition: LNMBackendRegisterInfo.h:53
ChimeraTK::TransferType
TransferType
Used to indicate the applicable operation on a Transferelement.
Definition: TransferElement.h:51
ChimeraTK::LNMBackend::UndecoratedParams::_numberOfWords
size_t _numberOfWords
Definition: LNMAccessorPlugin.h:18
ChimeraTK::LNMBackend::DoubleBufferPlugin::doRegisterInfoUpdate
void doRegisterInfoUpdate() override
Implementation of the plugin specific register information update.
Definition: LNMDoubleBufferPlugin.cc:21
ChimeraTK::LNMBackendRegisterInfo::targetType
TargetType targetType
Type of the target.
Definition: LNMBackendRegisterInfo.h:50
ChimeraTK::LNMBackend::DoubleBufferPlugin
Definition: LNMDoubleBufferPlugin.h:14
ChimeraTK::LNMBackend::DoubleBufferAccessorDecorator::doPreRead
void doPreRead(TransferType type) override
Definition: LNMDoubleBufferPlugin.cc:115
ChimeraTK::NDRegisterAccessorDecorator
Base class for decorators of the NDRegisterAccessor.
Definition: NDRegisterAccessorDecorator.h:120
ChimeraTK::LNMBackendRegisterInfo::_dataDescriptor
DataDescriptor _dataDescriptor
Definition: LNMBackendRegisterInfo.h:111
LNMDoubleBufferPlugin.h
ChimeraTK::LNMBackend::DoubleBufferAccessorDecorator
Definition: LNMDoubleBufferPlugin.h:41
BackendFactory.h
ChimeraTK::LNMBackendRegisterInfo::supportedFlags
AccessModeFlags supportedFlags
Supported AccessMode flags.
Definition: LNMBackendRegisterInfo.h:106
ChimeraTK::LNMBackendRegisterInfo::length
unsigned int length
The length of the range (i.e.
Definition: LNMBackendRegisterInfo.h:62
ChimeraTK::LNMBackend::DoubleBufferAccessorDecorator::doPostRead
void doPostRead(TransferType type, bool hasNewData) override
Definition: LNMDoubleBufferPlugin.cc:154
ChimeraTK::LNMBackend::DoubleBufferAccessorDecorator::doReadTransferSynchronously
void doReadTransferSynchronously() override
Definition: LNMDoubleBufferPlugin.cc:144
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::LNMBackend::DoubleBufferAccessorDecorator::mayReplaceOther
bool mayReplaceOther(const boost::shared_ptr< TransferElement const > &other) const override
Definition: LNMDoubleBufferPlugin.cc:212
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