ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
SubDomain.h
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#pragma once
4
5#include "../DeviceBackend.h"
6#include "../VersionNumber.h"
9
10#include <boost/make_shared.hpp>
11
12namespace ChimeraTK::async {
13 class MuxedInterruptDistributorFactory;
14 class MuxedInterruptDistributor;
15 class Domain;
16
17 template<typename UserType>
18 class AsyncNDRegisterAccessor;
19
20 namespace detail {
21 template<typename UserType, typename BackendSpecificDataType>
23 } // namespace detail
24
25 /********************************************************************************************************************/
26
32 template<typename BackendSpecificDataType>
33 class SubDomain : public boost::enable_shared_from_this<SubDomain<BackendSpecificDataType>> {
34 public:
35 SubDomain(boost::shared_ptr<DeviceBackend> backend, std::vector<size_t> qualifiedAsyncId,
36 boost::shared_ptr<MuxedInterruptDistributor> parent, boost::shared_ptr<Domain> domain);
37
38 void activate(BackendSpecificDataType, VersionNumber v);
39 void distribute(BackendSpecificDataType, VersionNumber v);
40 void sendException(const std::exception_ptr& e);
41
49 template<typename DistributorType>
50 boost::shared_ptr<AsyncAccessorManager> getAccessorManager(std::vector<size_t> const& qualifiedSubDomainId);
51
52 boost::shared_ptr<Domain> getDomain() { return _domain; }
53 std::vector<size_t> getId() { return _id; }
54 boost::shared_ptr<DeviceBackend> getBackend() { return _backend; }
55
56 template<typename UserType>
57 boost::shared_ptr<AsyncNDRegisterAccessor<UserType>> subscribe(
58 RegisterPath name, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags);
59
60 protected:
61 std::vector<size_t> _id;
62
63 boost::shared_ptr<DeviceBackend> _backend;
64 boost::weak_ptr<MuxedInterruptDistributor> _muxedInterruptDistributor;
65 boost::weak_ptr<TriggeredPollDistributor> _pollDistributor;
66 boost::weak_ptr<VariableDistributor<std::nullptr_t>> _variableDistributor;
67 boost::shared_ptr<MuxedInterruptDistributor> _parent;
68 boost::shared_ptr<Domain> _domain;
69
70 template<typename UserType, typename BackendDataType>
72 };
73
74 /********************************************************************************************************************/
75
76 namespace detail {
77 // Helper class to get instances for all user types. We cannot put the implementation into the header because of
78 // circular header inclusion, and we cannot write a "for all user types" macro for functions because of the return
79 // value and the function signature.
80 template<typename UserType, typename BackendSpecificDataType>
82 public:
83 static boost::shared_ptr<AsyncNDRegisterAccessor<UserType>> subscribeTo(
84 SubDomain<BackendSpecificDataType>& subDomain, RegisterPath name, size_t numberOfWords,
85 size_t wordOffsetInRegister, AccessModeFlags flags);
86 };
87
88 } // namespace detail
89
90 /********************************************************************************************************************/
91
92 template<typename BackendSpecificDataType>
93 SubDomain<BackendSpecificDataType>::SubDomain(boost::shared_ptr<DeviceBackend> backend,
94 std::vector<size_t> qualifiedAsyncId, boost::shared_ptr<MuxedInterruptDistributor> parent,
95 boost::shared_ptr<Domain> domain)
96 : _id(std::move(qualifiedAsyncId)), _backend(backend), _parent(std::move(parent)), _domain(std::move(domain)) {}
97
98 /********************************************************************************************************************/
99 template<typename BackendSpecificDataType>
100 template<typename UserType>
101 boost::shared_ptr<AsyncNDRegisterAccessor<UserType>> SubDomain<BackendSpecificDataType>::subscribe(
102 RegisterPath name, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
104 *this, name, numberOfWords, wordOffsetInRegister, flags);
105 }
106
107 /********************************************************************************************************************/
108
109 template<typename BackendSpecificDataType>
110 template<typename DistributorType>
111 boost::shared_ptr<AsyncAccessorManager> SubDomain<BackendSpecificDataType>::getAccessorManager(
112 std::vector<size_t> const& qualifiedSubDomainId) {
113 if(qualifiedSubDomainId.size() == 1) {
114 // return the distributor from this instance, not a from a SubDomain further down the tree
115
116 boost::weak_ptr<DistributorType>* weakDistributor{
117 nullptr}; // Cannot create a reference here, but references in the "if constexpr" scope are not seen outside
118
119 if constexpr(std::is_same<DistributorType, TriggeredPollDistributor>::value) {
120 weakDistributor = &_pollDistributor;
121 }
122 else if constexpr(std::is_same<DistributorType, VariableDistributor<BackendSpecificDataType>>::value) {
123 weakDistributor = &_variableDistributor;
124 }
125 else {
126 throw ChimeraTK::logic_error("SubDomain::getAccessorManager(): Wrong template parameter.");
127 }
128
129 auto distributor = weakDistributor->lock();
130 if(!distributor) {
131 distributor = boost::make_shared<DistributorType>(_backend, this->shared_from_this(), _domain);
132 *weakDistributor = distributor;
133 if(_domain->unsafeGetIsActive()) {
134 // Creating a new accessor in an activated domain is only supported if the BackendSpecificDataType is
135 // nullptr_t. At the moment there are two use cases we need:
136 //
137 // 1. BackendSpecificDataType is nullptr_t.
138 // - There are three distributors (TriggeredPollDistributor, VariableDistributor<nullptr_t> and the
139 // InterruptControllerHandler) and a hierarchy of TriggerDistributors.
140 // - You can get an accessor to one of the distributors and receive data (active domain), and then a second
141 // distributor is created.
142 // 2. The BackendSpecificDataType contains all required data.
143 // - There is no hierarchy of TriggerDistributors.
144 // - The VariableDistributor<BackendSpecificDataType> will be the only distributor and if it is not there,
145 // it means the domain has just been created and is not activated yet. As the VariableDistributor is
146 // holding the only ownership of the TriggerDistributor, both will go away together.
147 //
148 // At the moment the code does not support a combined option, which would require the option the get the
149 // initial value for the newly created distributor here.
150 if constexpr(std::is_same<BackendSpecificDataType, std::nullptr_t>::value) {
151 // In case the BackendSpecificDataType is nullptr_t, we know
152 // - the initial value is nullptr
153 // - the version number cannot be determined from the data and we have to invent a new version number here
154 distributor->distribute(nullptr, {});
155 }
156 else {
157 // To put an implementation here, we need a way to get an initial value
158 // (for instance from the domain, see https://redmine.msktools.desy.de/issues/13038).
159 // If you run into this assertion, chances are that you accidentally ran into this code branch because the
160 // domain has been activated too early due to a bug.
161 assert(false);
162 }
163 }
164 }
165 return distributor;
166 }
167 // get a distributor from further down the tree, behind one or more MuxedInterruptDistributors
168 auto muxedInterruptDistributor = _muxedInterruptDistributor.lock();
169 if(!muxedInterruptDistributor) {
170 muxedInterruptDistributor =
172 _muxedInterruptDistributor = muxedInterruptDistributor;
173 if(_domain->unsafeGetIsActive()) {
174 // As for the distributor<nullptr_t> we activate using a new version number, as no version is strictly related
175 // to the "data".
176 muxedInterruptDistributor->activate({});
177 }
178 }
179
180 return muxedInterruptDistributor->getAccessorManager<DistributorType>(
181 {++qualifiedSubDomainId.begin(), qualifiedSubDomainId.end()});
182 }
183
184 /********************************************************************************************************************/
185
186 template<typename BackendSpecificDataType>
187 void SubDomain<BackendSpecificDataType>::distribute(BackendSpecificDataType data, VersionNumber version) {
188 if(!_domain->unsafeGetIsActive()) {
189 return;
190 }
191 auto pollDistributor = _pollDistributor.lock();
192 if(pollDistributor) {
193 // Distribute to pollDistributor first, as it may modify the VersionNumber if a DataConsistencyRealm is used,
194 // and the variableDistributor should use the same VersionNumber so a directly subscribed (void) interrupt has
195 // the same VersionNumber as data polled with the same interrupt.
196 version = pollDistributor->distribute(nullptr, version);
197 }
198 auto muxedInterruptDistributor = _muxedInterruptDistributor.lock();
199 if(muxedInterruptDistributor) {
200 muxedInterruptDistributor->handle(version);
201 }
202 auto variableDistributor = _variableDistributor.lock();
203 if(variableDistributor) {
204 variableDistributor->distribute(data, version);
205 }
206 }
207
208 /********************************************************************************************************************/
209
210 template<typename BackendSpecificDataType>
211 void SubDomain<BackendSpecificDataType>::activate(BackendSpecificDataType data, VersionNumber version) {
212 auto pollDistributor = _pollDistributor.lock();
213 if(pollDistributor) {
214 version = pollDistributor->distribute(nullptr, version);
215 }
216 auto muxedInterruptDidstributor = _muxedInterruptDistributor.lock();
217 if(muxedInterruptDidstributor) {
218 muxedInterruptDidstributor->activate(version);
219 }
220 auto variableDistributor = _variableDistributor.lock();
221 if(variableDistributor) {
222 variableDistributor->distribute(data, version);
223 }
224 }
225
226 /********************************************************************************************************************/
227
228 template<typename BackendSpecificDataType>
229 void SubDomain<BackendSpecificDataType>::sendException(const std::exception_ptr& e) {
230 auto pollDistributor = _pollDistributor.lock();
231 if(pollDistributor) {
232 pollDistributor->sendException(e);
233 }
234 auto muxedInterruptDistributor = _muxedInterruptDistributor.lock();
235 if(muxedInterruptDistributor) {
236 muxedInterruptDistributor->sendException(e);
237 }
238 auto variableDistributor = _variableDistributor.lock();
239 if(variableDistributor) {
240 variableDistributor->sendException(e);
241 }
242 }
243
244 /********************************************************************************************************************/
245
246 namespace detail {
247
248 template<typename UserType, typename BackendSpecificDataType>
249 boost::shared_ptr<AsyncNDRegisterAccessor<UserType>> SubDomainSubscriptionImplementor<UserType,
250 BackendSpecificDataType>::subscribeTo(SubDomain<BackendSpecificDataType>& subDomain, RegisterPath name,
251 size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
252 auto registerInfo = subDomain._backend->getRegisterCatalogue().getRegister(name);
253
254 // Find the right place in the distribution tree to subscribe
255 boost::shared_ptr<AsyncAccessorManager> distributor;
256 if constexpr(std::is_same<BackendSpecificDataType, std::nullptr_t>::value) {
257 // Special implementation for data type nullptr_t: Use a poll distributor if the data is not
258 // FundamentalType::nodata itself
259 if(registerInfo.getDataDescriptor().fundamentalType() == DataDescriptor::FundamentalType::nodata) {
260 distributor = subDomain.template getAccessorManager<VariableDistributor<std::nullptr_t>>(
261 registerInfo.getQualifiedAsyncId());
262 }
263 else {
264 distributor =
265 subDomain.template getAccessorManager<TriggeredPollDistributor>(registerInfo.getQualifiedAsyncId());
266 }
267 }
268 else {
269 // For all other BackendSpecificDataType use the according VariableDistributor.
270 // This scheme might need some improvement later.
271 distributor = subDomain.template getAccessorManager<VariableDistributor<BackendSpecificDataType>>(
272 registerInfo.getQualifiedAsyncId());
273 }
274
275 return distributor->template subscribe<UserType>(name, numberOfWords, wordOffsetInRegister, flags);
276 }
277
279 } // namespace detail
280
281 /********************************************************************************************************************/
282
283} // namespace ChimeraTK::async
#define INSTANTIATE_MULTI_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass,...)
Set of AccessMode flags with additional functionality for an easier handling.
Definition AccessMode.h:48
Class to store a register path name.
Class for generating and holding version numbers without exposing a numeric representation.
static MuxedInterruptDistributorFactory & getInstance()
boost::shared_ptr< MuxedInterruptDistributor > createMuxedInterruptDistributor(boost::shared_ptr< SubDomain< std::nullptr_t > > parent)
Send backend-specific asynchronous data to different distributors:
Definition SubDomain.h:33
boost::weak_ptr< MuxedInterruptDistributor > _muxedInterruptDistributor
Definition SubDomain.h:64
std::vector< size_t > getId()
Definition SubDomain.h:53
boost::weak_ptr< VariableDistributor< std::nullptr_t > > _variableDistributor
Definition SubDomain.h:66
boost::shared_ptr< Domain > getDomain()
Definition SubDomain.h:52
boost::shared_ptr< Domain > _domain
Definition SubDomain.h:68
SubDomain(boost::shared_ptr< DeviceBackend > backend, std::vector< size_t > qualifiedAsyncId, boost::shared_ptr< MuxedInterruptDistributor > parent, boost::shared_ptr< Domain > domain)
Definition SubDomain.h:93
void sendException(const std::exception_ptr &e)
Definition SubDomain.h:229
boost::shared_ptr< AsyncAccessorManager > getAccessorManager(std::vector< size_t > const &qualifiedSubDomainId)
Get an AsyncAccessorManager for a specific SubDomain.
Definition SubDomain.h:111
boost::shared_ptr< DeviceBackend > _backend
Definition SubDomain.h:63
boost::shared_ptr< AsyncNDRegisterAccessor< UserType > > subscribe(RegisterPath name, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Definition SubDomain.h:101
boost::weak_ptr< TriggeredPollDistributor > _pollDistributor
Definition SubDomain.h:65
boost::shared_ptr< DeviceBackend > getBackend()
Definition SubDomain.h:54
std::vector< size_t > _id
Definition SubDomain.h:61
boost::shared_ptr< MuxedInterruptDistributor > _parent
Definition SubDomain.h:67
void distribute(BackendSpecificDataType, VersionNumber v)
Definition SubDomain.h:187
void activate(BackendSpecificDataType, VersionNumber v)
Definition SubDomain.h:211
static boost::shared_ptr< AsyncNDRegisterAccessor< UserType > > subscribeTo(SubDomain< BackendSpecificDataType > &subDomain, RegisterPath name, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Definition SubDomain.h:250
Exception thrown when a logic error has occured.
Definition Exception.h:51
STL namespace.