ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
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
8namespace 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 // Note: TransferElement Spec E.6.1 dictates that the version number and data validity needs to be set before this
190 // check.
191 if(!hasNewData) {
192 return;
193 }
194
195 if(_currentBuffer) {
196 for(size_t i = 0; i < _target->getNumberOfChannels(); ++i) {
197 ChimeraTK::NDRegisterAccessorDecorator<UserType>::buffer_2D[i].swap(_target->accessChannel(i));
198 }
199 }
200 else {
201 for(size_t i = 0; i < _secondBufferReg->getNumberOfChannels(); ++i) {
202 ChimeraTK::NDRegisterAccessorDecorator<UserType>::buffer_2D[i].swap(_secondBufferReg->accessChannel(i));
203 }
204 }
205 }
206
207 template<typename UserType>
208 std::vector<boost::shared_ptr<TransferElement>> DoubleBufferAccessorDecorator<
209 UserType>::getHardwareAccessingElements() {
210 // returning only this means the DoubleBufferAccessorDecorator will not be optimized when put into TransferGroup
211 // optimizing would break our handshake protocol, since it reorders transfers
212 return {TransferElement::shared_from_this()};
213 }
214
215 template<typename UserType>
217 const boost::shared_ptr<const TransferElement>& other) const {
218 // we need this to support merging of accessors using the same double-buffered as target.
219 // If other is also double-buffered region belonging to the same plugin instance, allow the merge
220 auto otherDoubleBuffer = boost::dynamic_pointer_cast<DoubleBufferAccessorDecorator const>(other);
221 if(otherDoubleBuffer.get() == this) {
222 return false;
223 }
224 if(!otherDoubleBuffer) {
225 return false;
226 }
227 return &(otherDoubleBuffer->_plugin) == &_plugin;
228 }
229} // namespace ChimeraTK::LNMBackend
void remove(AccessMode flag)
Remove the given flag from the set.
Definition AccessMode.cc:56
void setRawDataType(const DataType &d)
Set the raw data type.
@ none
The data type/concept does not exist, e.g. there is no raw transfer (do not confuse with Void)
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.
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
void doPostRead(TransferType type, bool hasNewData) override
Backend specific implementation of postRead().
DoubleBufferAccessorDecorator(boost::shared_ptr< LogicalNameMappingBackend > &backend, boost::shared_ptr< NDRegisterAccessor< UserType > > &target, DoubleBufferPlugin &plugin, const UndecoratedParams &accessorParams)
bool mayReplaceOther(const boost::shared_ptr< TransferElement const > &other) const override
Check whether the TransferElement can be used in places where the TransferElement "other" is currentl...
void doPreRead(TransferType type) override
Backend specific implementation of preRead().
void doRegisterInfoUpdate() override
Implementation of the plugin specific register information update.
DoubleBufferPlugin(const LNMBackendRegisterInfo &info, size_t pluginIndex, std::map< std::string, std::string > parameters)
boost::shared_ptr< NDRegisterAccessor< UserType > > decorateAccessor(boost::shared_ptr< LogicalNameMappingBackend > &backend, boost::shared_ptr< NDRegisterAccessor< TargetType > > &target, const UndecoratedParams &accessorParams)
RegisterInfo structure for the LogicalNameMappingBackend.
unsigned int firstIndex
The first index in the range.
AccessModeFlags supportedFlags
Supported AccessMode flags.
bool writeable
Flag if the register is writeable.
unsigned int length
The length of the range (i.e.
std::string deviceName
The target device alias.
TargetType targetType
Type of the target.
Base class for decorators of the NDRegisterAccessor.
N-dimensional register accessor.
Exception thrown when a logic error has occured.
Definition Exception.h:51
@ 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.
Helper struct to hold extra parameters needed by some plugins, used in decorateAccessor()