ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
DataConsistencyGroupHistorizedMatcher.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
7#include "ReadAnyGroup.h"
9
11
12 /********************************************************************************************************************/
13
14 boost::shared_ptr<TransferElement> HistorizedMatcher::decorateAccessor(TransferElementAbstractor& acc) {
15 boost::shared_ptr<TransferElement> dataConsistencyDecorator;
16 callForType(acc.getValueType(), [&](auto t) {
17 using UserType = decltype(t);
18
19 // check if accessor already is in another DataConsistencyGroup
20 for(auto& e : acc.getInternalElements()) {
21 auto dec = boost::dynamic_pointer_cast<DataConsistencyDecorator<UserType>>(e);
22 if(dec) {
23 throw ChimeraTK::logic_error(
24 "accessor is already in historized DataConsistencyGroup, cannot add it to another one: " + acc.getName());
25 }
26 }
27 auto accImpl = boost::dynamic_pointer_cast<NDRegisterAccessor<UserType>>(acc.getHighLevelImplElement());
28 assert(accImpl);
29 // factory function which creates our DataConsistencyDecorator
30 auto factoryF = [&](const boost::shared_ptr<NDRegisterAccessor<UserType>>& toBeDecorated) {
31 return boost::make_shared<DataConsistencyDecorator<UserType>>(toBeDecorated, this);
32 };
33 // in case accImpl is ApplicationCore's MetaDataPropagatingRegisterDecorator we need to "go inside" and
34 // replace its target by our DataConsistencyDecorator
35 dataConsistencyDecorator = accImpl->decorateDeepInside(factoryF);
36 if(!dataConsistencyDecorator) {
37 // accImpl is not itself a decorator, so decorateDeepInside did not do anything.
38 dataConsistencyDecorator = factoryF(accImpl);
39 acc.replace(dataConsistencyDecorator);
40 }
41 });
42 return dataConsistencyDecorator;
43 }
44
45 /********************************************************************************************************************/
46
47 bool HistorizedMatcher::checkUpdate(const TransferElementID& transferElementID) {
48 auto it = _targetElements.find(transferElementID);
49 assert(it != _targetElements.end());
50
51 auto& pElem = it->second;
52 auto vn = pElem.acc.getVersionNumber();
53 if(pElem.histLen > 0 && vn == pElem.versionNumbers[0]) {
54 // take special care for duplicate VersionNumbers. We want VersionNumbers only once in history.
55 // So in case of a duplicate VersionNumber, we mark the previous historized value as invalid
56 pElem.versionNumbers[0] = VersionNumber(nullptr);
57 }
58
59 bool consistent = findMatch(transferElementID);
60 return consistent;
61 }
62
63 /********************************************************************************************************************/
64
65 void HistorizedMatcher::handleMissingPreReads(TransferElementID callerId) {
66 // prevent recursion by setting flag
67 if(_handleMissingPreReadsCalled) {
68 return;
69 }
70 _handleMissingPreReadsCalled = true;
71 auto resetFlag = cppext::finally([this] { _handleMissingPreReadsCalled = false; });
72
73 // just a check for right usage: check that DataConsistencyGroup::update was called on updates from ReadAnyGroup.
74 if(_decoratorsNeedPreRead) {
75 // we know there was already a consistent data update handed out from ReadAnyGroup
76 if(lastUpdateCall() != callerId) {
77 throw ChimeraTK::logic_error("updates from ReadAnyGroup must be processed by DataConsistencyGroup::update");
78 }
79 }
80
81 for(auto& e : _pushElements) {
82 if(e.first != callerId) {
83 auto& acc = e.second;
84 acc.getHighLevelImplElement()->preRead(TransferType::read);
85 }
86 }
87 _decoratorsNeedPreRead = false;
88 }
89
90 /********************************************************************************************************************/
91
92 void HistorizedMatcher::handleMissingPostReads(TransferElementID callerId, bool updateBuffer) {
93 // prevent recursion by setting flag
94 if(_handleMissingPostReadsCalled) {
95 return;
96 }
97 _handleMissingPostReadsCalled = true;
98 auto resetFlag = cppext::finally([this] { _handleMissingPostReadsCalled = false; });
99
100 // To update other user buffers, call postRead on the other involved decorators.
101 // This concerns all pushElements, except when a push element was already on right version num and received
102 // another update, which can only happen if the new datum has the same version number (handled as special case).
103 // Note, in case of an exception thrown by some postRead, it might happen that postRead is
104 // called more than once in a row, for the other elements. This is allowed.
105 for(auto& e : _pushElements) {
106 if(e.first != callerId) {
107 auto& acc = e.second;
108 acc.getHighLevelImplElement()->postRead(TransferType::read, updateBuffer);
109 }
110 }
111 _decoratorsNeedPreRead = true;
112 }
113
114 /********************************************************************************************************************/
115
116 void HistorizedMatcher::add(TransferElementAbstractor& acc, unsigned histLen) {
117 auto* rag = acc.getReadAnyGroup();
118 auto id = acc.getId();
119
120 auto dataConsistencyDecorator = decorateAccessor(acc);
121 auto target = dataConsistencyDecorator->getInternalElements().front();
122 setupHistory(TransferElementAbstractor{target}, histLen);
123 // add decorated access to our elements map (key = Id remains unchanged by decoration)
124 _pushElements[id] = acc;
125 if(rag) {
126 // also find the copy of accessor abstractor in ReadAnyGroup and decorate it in there
127 for(auto& pe : rag->push_elements) {
128 if(pe.getId() == id) {
129 pe.replace(acc);
130 }
131 }
132 }
133 }
134
135 /********************************************************************************************************************/
136
137 void HistorizedMatcher::setupHistory(const TransferElementAbstractor& acc, unsigned histLen) {
138 TransferElementID id = acc.getId();
139 if(_targetElements.find(id) != _targetElements.end()) {
140 // was alread set up
141 return;
142 }
143
144 callForType(acc.getValueType(), [&](auto argForType) {
145 // set up ring buffer for element's user type
146 using UserType = decltype(argForType);
147 using UserBufferType = std::vector<std::vector<UserType>>;
148 // prepare and insert PushElement not yet having memory (because getUserBuffer requires registered accessor)
149 TargetElement element0 = {acc, histLen, nullptr, typeid(UserType), {}, {}};
150 _targetElements.insert({id, element0});
151 if(histLen > 0) {
152 auto* mem = new std::vector<UserBufferType>(histLen);
153 // get user buffer just to find out it's shape
154 auto& buf = getUserBuffer<UserType>(id);
155 unsigned nChannels = buf.size();
156 assert(nChannels > 0);
157 unsigned nSamples = buf[0].size();
158 for(UserBufferType& historyElement : *mem) {
159 historyElement.resize(nChannels);
160 for(auto& historyElementChannel : historyElement) {
161 historyElementChannel.resize(nSamples);
162 }
163 }
164 // continue setup: make buffer known
165 TargetElement& element = _targetElements.at(id);
166 element.histBuffer = mem;
167 element.versionNumbers.resize(histLen);
168 std::fill(element.versionNumbers.begin(), element.versionNumbers.end(), VersionNumber{nullptr});
169 element.dataValidities.resize(histLen);
170 }
171 });
172 }
173
174 /********************************************************************************************************************/
175
176 HistorizedMatcher::~HistorizedMatcher() {
177 for(auto& x : _targetElements) {
178 TransferElementID id = x.first;
179 TargetElement& element = x.second;
180 if(element.histLen > 0) {
181 try {
182 callForType(element.histBufferType, [&](auto arg) {
183 using UserType = decltype(arg);
184 using UserBufferType = std::vector<std::vector<UserType>>;
185 delete getBufferVector<UserBufferType>(id);
186 });
187 }
188 catch(std::bad_cast& e) {
189 // catch + assert in order to satisfy linter
190 assert(false);
191 }
192 }
193 }
194 }
195
196 /********************************************************************************************************************/
197
198 bool HistorizedMatcher::findMatch(TransferElementID transferElementID) {
199 auto it = _targetElements.find(transferElementID);
200 if(it == _targetElements.end()) {
201 // ignore unknown transfer elements
202 return false;
203 }
204 TargetElement& theElement = it->second;
205 auto vn = theElement.acc.getVersionNumber();
206
207 for(auto& pair : _targetElements) {
208 if(pair.first == transferElementID) {
209 continue;
210 }
211 TargetElement& element = pair.second;
212 // first consider accessor's user buffer and version number
213 if(element.acc.getVersionNumber() == vn) {
214 element.lastMatchingIndex = 0;
215 }
216 else if(element.histLen > 0) {
217 auto versionNumVector = element.versionNumbers;
218
219 auto pos = std::find(versionNumVector.begin(), versionNumVector.end(), vn);
220 if(pos == versionNumVector.end()) {
221 return false;
222 }
223 element.lastMatchingIndex = pos - versionNumVector.begin() + 1;
224 }
225 else {
226 // no direct match and no history
227 return false;
228 }
229 }
230 theElement.lastMatchingIndex = 0;
231 _lastMatchingVersionNumber = vn;
232 return true;
233 }
234
235 /********************************************************************************************************************/
236
237 void HistorizedMatcher::updateHistory(TransferElementID transferElementID) {
238 TargetElement& element = _targetElements.at(transferElementID);
239 if(element.histLen == 0) {
240 // exit early if history not availabe for this element
241 return;
242 }
243
244 VersionNumber vn = element.acc.getVersionNumber();
245 DataValidity dv = element.acc.dataValidity();
246
247 callForType(element.histBufferType, [&](auto arg) {
248 using UserType = decltype(arg);
249 using UserBufferType = std::vector<std::vector<UserType>>;
250 auto& buf = getUserBuffer<UserType>(transferElementID);
251 // update history of currentBunchPattern so we can use it in findBunchPattern()
252
253 auto& bufferVector = *getBufferVector<UserBufferType>(transferElementID);
254 unsigned histLen = bufferVector.size();
255 auto& versionNumVector = element.versionNumbers;
256 auto& datavalidityVector = element.dataValidities;
257
258 // swap data into history
259 // after all swaps, to be erased data is in user buffer. Usually this is the oldest data element,
260 // except in special case where first history element is invalid.
261 if(versionNumVector[0] > VersionNumber(nullptr)) {
262 for(unsigned i = histLen - 1; i > 0; i--) {
263 bufferVector[i].swap(bufferVector[i - 1]);
264 versionNumVector[i] = versionNumVector[i - 1];
265 datavalidityVector[i] = datavalidityVector[i - 1];
266 }
267 }
268 bufferVector[0].swap(buf);
269 versionNumVector[0] = vn;
270 datavalidityVector[0] = dv;
271 });
272 }
273
274 /********************************************************************************************************************/
275
276 void HistorizedMatcher::getMatchingInfo(TransferElementID id, VersionNumber& vs, DataValidity& dv) {
277 TargetElement& pe = _targetElements.at(id);
278
279 if(pe.lastMatchingIndex > 0) {
280 vs = pe.versionNumbers[pe.lastMatchingIndex - 1];
281 dv = pe.dataValidities[pe.lastMatchingIndex - 1];
282 return;
283 }
284 vs = pe.acc.getVersionNumber();
285 dv = pe.acc.dataValidity();
286 }
287
288 /********************************************************************************************************************/
289
290} // namespace ChimeraTK::DataConsistencyGroupDetail
boost::shared_ptr< TransferElement > decorateAccessor(TransferElementAbstractor &acc)
decorate accessor by replacing its target => DataConsistencyDecorator(target), possibly at an inner l...
N-dimensional register accessor.
Base class for register accessors abstractors independent of the UserType.
ChimeraTK::VersionNumber getVersionNumber() const
Returns the version number that is associated with the last transfer (i.e.
const boost::shared_ptr< TransferElement > & getHighLevelImplElement()
Obtain the highest level implementation TransferElement.
const std::type_info & getValueType() const
Returns the std::type_info for the value type of this transfer element.
ReadAnyGroup * getReadAnyGroup() const
Obtain the ReadAnyGroup this TransferElement is part of, or nullptr if not in a ReadAnyGroup.
DataValidity dataValidity() const
Return current validity of the data.
void replace(const TransferElementAbstractor &newAccessor)
Assign a new accessor to this TransferElementAbstractor.
TransferElementID getId() const
Obtain unique ID for the actual implementation of this TransferElement.
Simple class holding a unique ID for a TransferElement.
Class for generating and holding version numbers without exposing a numeric representation.
Exception thrown when a logic error has occured.
Definition Exception.h:51
void callForType(const std::type_info &type, LAMBDATYPE lambda)
Helper function for running code which uses some compile-time type that is specified at runtime as a ...
DataValidity
The current state of the data.
unsigned lastMatchingIndex
match indices set by findMatch() in case it returns true.