ChimeraTK-DeviceAccess 03.31.00
Loading...
Searching...
No Matches
ReadCoordinator.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 "ReadCoordinator.h"
5
7
8#include <cstring>
9#include <iterator>
10
11namespace ChimeraTK::detail {
12
13 /********************************************************************************************************************/
14
15 bool ReadCoordinator::empty() const {
16 return _groupIndex.empty();
17 }
18
19 /********************************************************************************************************************/
20
21 void ReadCoordinator::run(const std::vector<uint8_t>& rawBuffer) {
22 _rawBuffer = &rawBuffer;
23 for(auto& entry : _groups) {
24 auto& group = *entry.second;
25 if(group.consumersEmpty()) {
26 continue; // the group lost all its consumers; it stays registered until teardown
27 }
28 if(group.isRaw) {
29 // Raw-mode consumers are copied without conversion: one typed copy per consumer, up to its sample count.
30 group.rawCopy(rawBuffer);
31 }
32 else {
33 // One type-erased call per group per read; the typed conversion loop runs inside doPostReadImpl.
34 group.converterLoopHelper->doPostRead();
35 }
36 }
37 _rawBuffer = nullptr;
38 }
39
40 /********************************************************************************************************************/
41
42 template<class UserType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
43 bool isSigned>
44 void ReadCoordinator::doPostReadImpl(
45 RawConverter::Converter<UserType, RawType, sc, fc, isSigned> converter, size_t groupId) {
46 // The groupId identifies the group within _groups. It is valid for the whole lifetime of the coordinator, and the
47 // group is a Group<UserType> (the helper was created with the matching UserType). Each consumer's staging buffer
48 // is resolved once per read (a wrapping decorator may swap it on every read), so there is no per-sample indirect
49 // call in the typed conversion loop.
50 // A raw type of ChimeraTK::Void means a void register. Its low-level element has no data bytes, and the conversion
51 // is a no-op (Converter::toCooked() returns a default-constructed UserType), so the buffer is not touched and the
52 // loop is skipped entirely. This mirrors the guard of the accessor's former in-place loop.
53 if constexpr(!std::is_same_v<RawType, ChimeraTK::Void>) {
54 auto& group = static_cast<Group<UserType>&>(*_groups.at(groupId));
55 const uint8_t* raw = _rawBuffer->data();
56 for(auto& consumer : group.consumers) {
57 if(consumer.nSamples == 0) {
58 continue;
59 }
60 auto* cooked = consumer.staging.data();
61 const auto* rawSample = raw + consumer.byteOffset;
62 for(size_t i = 0; i < consumer.nSamples; ++i) {
63 RawType temp;
64 std::memcpy(&temp, rawSample + i * consumer.stride, sizeof(RawType));
65 cooked[i] = converter.toCooked(temp);
66 }
67 }
68 }
69 }
70
71 /********************************************************************************************************************/
72
73 template<class UserType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
74 bool isSigned>
75 void ReadCoordinator::doPreWriteImpl(
76 RawConverter::Converter<UserType, RawType, sc, fc, isSigned> /*converter*/, size_t /*groupId*/) {
77 // The coordinator is read-only; the ConverterLoopHelper interface requires a doPreWrite, which is never invoked.
78 }
79
80 /********************************************************************************************************************/
81
82 template<typename UserType>
83 ReadCoordinator::Registration<UserType>::Registration(ReadCoordinator* owner,
84 const NumericAddressedRegisterInfo& info, bool isRaw, size_t byteOffset, size_t stride, size_t nSamples)
85 : _owner(owner), _staging(std::make_unique<std::vector<UserType>>(nSamples)) {
86 // Same grouping as the 2D accessor: converter information plus UserType; raw-mode consumers are grouped separately
87 // by their UserType and copied without conversion.
88 const auto& channel = info.channels[0];
89 GroupKey key{channel.dataType, channel.width, channel.nFractionalBits, channel.signedFlag, channel.rawType,
90 std::type_index(typeid(UserType)), isRaw};
91
92 // Resolve the group: insert a new one if this key is not yet registered, otherwise reuse the existing group. The
93 // consumer stores a reference to the heap-allocated staging vector, whose address never changes even when this
94 // registration is moved.
95 auto [groupIt, inserted] = _owner->_groupIndex.emplace(key, 0);
96 if(inserted) {
97 // Give the new group a stable, monotonically increasing groupId. It is never reused, so the implParameter of any
98 // group's ConverterLoopHelper stays valid for the group's lifetime.
99 _groupId = _owner->_nextGroupId++;
100 groupIt->second = _groupId;
101 auto group = std::make_unique<Group<UserType>>();
102 group->isRaw = isRaw;
103 if(!group->isRaw) {
104 // The group's typed conversion loop: one ConverterLoopHelper, cloned from the 2D accessor's pattern.
105 //
106 // The implParameter is the group's stable groupId, so doPostReadImpl resolves the group by identity (no
107 // pointer/address-based or position-based lookup).
108 // The full register info (including the register name) and channel index 0 are passed on so RawConverter
109 // error messages keep the register name and channel for the read path.
110 //
111 // The general, void-aware factory is used rather than the fixed-raw variant: it handles both
112 // FundamentalType::nodata (raw type ChimeraTK::Void) and a UserType of ChimeraTK::Void, so RawConverter.h
113 // stays unchanged. It is called directly, without callForRawType: that helper is a no-op for DataType::Void, so
114 // a void register would leave the helper unset. The factory's own callWithConverterParams resolves the raw type
115 // and covers the void cases, so the helper is always created here.
116 group->converterLoopHelper =
117 RawConverter::ConverterLoopHelper::makeConverterLoopHelper<UserType>(info, 0, _groupId, *owner);
118 }
119 _owner->_groups.emplace(_groupId, std::move(group));
120 }
121 else {
122 _groupId = groupIt->second;
123 }
124 auto& group = static_cast<Group<UserType>&>(*_owner->_groups.at(_groupId));
125 group.consumers.push_back(Consumer<UserType>(byteOffset, stride, nSamples, *_staging));
126 _it = std::prev(group.consumers.end());
127 }
128
129 /********************************************************************************************************************/
130
131 template<typename UserType>
132 // NOLINTNEXTLINE(clang-diagnostic-dtor-name) - linter is wrong, this is standard-conforming
133 ReadCoordinator::Registration<UserType>::~Registration() {
134 reset();
135 }
136
137 /********************************************************************************************************************/
138
139 template<typename UserType>
140 ReadCoordinator::Registration<UserType>::Registration(Registration&& other) noexcept
141 : _owner(other._owner), _groupId(other._groupId), _it(other._it), _staging(std::move(other._staging)) {
142 other._owner = nullptr;
143 }
144
145 /********************************************************************************************************************/
146
147 template<typename UserType>
148 ReadCoordinator::Registration<UserType>& ReadCoordinator::Registration<UserType>::operator=(
149 Registration&& other) noexcept {
150 if(this == &other) {
151 return *this;
152 }
153 reset();
154 _owner = other._owner;
155 _groupId = other._groupId;
156 _it = other._it;
157 _staging = std::move(other._staging);
158 other._owner = nullptr;
159 return *this;
160 }
161
162 /********************************************************************************************************************/
163
164 template<typename UserType>
165 void ReadCoordinator::Registration<UserType>::reset() {
166 if(_owner == nullptr) {
167 return;
168 }
169 // Only the consumer is removed; the group itself stays registered until teardown, so no group's groupId (and hence
170 // no group's ConverterLoopHelper implParameter) can ever be invalidated.
171 auto& group = static_cast<Group<UserType>&>(*_owner->_groups.at(_groupId));
172 group.consumers.erase(_it);
173 _owner = nullptr;
174 _staging.reset();
175 }
176
177 /********************************************************************************************************************/
178
179 template<typename UserType>
180 std::vector<UserType>& ReadCoordinator::Registration<UserType>::staging() {
181 return *_staging;
182 }
183
184 /********************************************************************************************************************/
185
186 // The Registration member functions are not performance critical (they only run when consumers are registered or
187 // unregistered), so they are not inlined into the class definition but defined here and explicitly instantiated for
188 // all supported user types.
189
190 INSTANTIATE_TEMPLATE_FOR_CHIMERATK_USER_TYPES(ReadCoordinator::Registration);
191
192 /********************************************************************************************************************/
193
194} // namespace ChimeraTK::detail
#define INSTANTIATE_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass)
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
STL namespace.
constexpr size_t nSamples