ChimeraTK-DeviceAccess  03.18.00
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"
8 #include "VariableDistributor.h"
9 
10 #include <boost/make_shared.hpp>
11 
12 namespace 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>
81  class SubDomainSubscriptionImplementor {
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  pollDistributor->distribute(nullptr, version);
194  }
195  auto muxedInterruptDistributor = _muxedInterruptDistributor.lock();
196  if(muxedInterruptDistributor) {
197  muxedInterruptDistributor->handle(version);
198  }
199  auto variableDistributor = _variableDistributor.lock();
200  if(variableDistributor) {
201  variableDistributor->distribute(data, version);
202  }
203  }
204 
205  /********************************************************************************************************************/
206 
207  template<typename BackendSpecificDataType>
208  void SubDomain<BackendSpecificDataType>::activate(BackendSpecificDataType data, VersionNumber version) {
209  auto pollDistributor = _pollDistributor.lock();
210  if(pollDistributor) {
211  pollDistributor->distribute(nullptr, version);
212  }
213  auto muxedInterruptDidstributor = _muxedInterruptDistributor.lock();
214  if(muxedInterruptDidstributor) {
215  muxedInterruptDidstributor->activate(version);
216  }
217  auto variableDistributor = _variableDistributor.lock();
218  if(variableDistributor) {
219  variableDistributor->distribute(data, version);
220  }
221  }
222 
223  /********************************************************************************************************************/
224 
225  template<typename BackendSpecificDataType>
226  void SubDomain<BackendSpecificDataType>::sendException(const std::exception_ptr& e) {
227  auto pollDistributor = _pollDistributor.lock();
228  if(pollDistributor) {
229  pollDistributor->sendException(e);
230  }
231  auto muxedInterruptDistributor = _muxedInterruptDistributor.lock();
232  if(muxedInterruptDistributor) {
233  muxedInterruptDistributor->sendException(e);
234  }
235  auto variableDistributor = _variableDistributor.lock();
236  if(variableDistributor) {
237  variableDistributor->sendException(e);
238  }
239  }
240 
241  /********************************************************************************************************************/
242 
243  namespace detail {
244 
245  template<typename UserType, typename BackendSpecificDataType>
246  boost::shared_ptr<AsyncNDRegisterAccessor<UserType>> SubDomainSubscriptionImplementor<UserType,
247  BackendSpecificDataType>::subscribeTo(SubDomain<BackendSpecificDataType>& subDomain, RegisterPath name,
248  size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
249  auto registerInfo = subDomain._backend->getRegisterCatalogue().getRegister(name);
250 
251  // Find the right place in the distribution tree to subscribe
252  boost::shared_ptr<AsyncAccessorManager> distributor;
253  if constexpr(std::is_same<BackendSpecificDataType, std::nullptr_t>::value) {
254  // Special implementation for data type nullptr_t: Use a poll distributor if the data is not
255  // FundamentalType::nodata itself
256  if(registerInfo.getDataDescriptor().fundamentalType() == DataDescriptor::FundamentalType::nodata) {
257  distributor = subDomain.template getAccessorManager<VariableDistributor<std::nullptr_t>>(
258  registerInfo.getQualifiedAsyncId());
259  }
260  else {
261  distributor =
262  subDomain.template getAccessorManager<TriggeredPollDistributor>(registerInfo.getQualifiedAsyncId());
263  }
264  }
265  else {
266  // For all other BackendSpecificDataType use the according VariableDistributor.
267  // This scheme might need some improvement later.
268  distributor = subDomain.template getAccessorManager<VariableDistributor<BackendSpecificDataType>>(
269  registerInfo.getQualifiedAsyncId());
270  }
271 
272  return distributor->template subscribe<UserType>(name, numberOfWords, wordOffsetInRegister, flags);
273  }
274 
276  } // namespace detail
277 
278  /********************************************************************************************************************/
279 
280 } // namespace ChimeraTK::async
ChimeraTK::async::detail::INSTANTIATE_MULTI_TEMPLATE_FOR_CHIMERATK_USER_TYPES
INSTANTIATE_MULTI_TEMPLATE_FOR_CHIMERATK_USER_TYPES(SubDomainSubscriptionImplementor, std::nullptr_t)
ChimeraTK::async::MuxedInterruptDistributorFactory::createMuxedInterruptDistributor
boost::shared_ptr< MuxedInterruptDistributor > createMuxedInterruptDistributor(boost::shared_ptr< SubDomain< std::nullptr_t >> parent)
Definition: MuxedInterruptDistributor.cc:68
ChimeraTK::async::SubDomain::getDomain
boost::shared_ptr< Domain > getDomain()
Definition: SubDomain.h:52
ChimeraTK::async::SubDomain::distribute
void distribute(BackendSpecificDataType, VersionNumber v)
Definition: SubDomain.h:187
ChimeraTK::async::SubDomain
Send backend-specific asynchronous data to different distributors:
Definition: MuxedInterruptDistributor.h:23
ChimeraTK::async::MuxedInterruptDistributorFactory::getInstance
static MuxedInterruptDistributorFactory & getInstance()
Definition: MuxedInterruptDistributor.cc:27
ChimeraTK::async
Definition: design_AsyncNDRegisterAcessor_and_NumericAddressedBackend.dox:1
ChimeraTK::async::SubDomain::_id
std::vector< size_t > _id
Definition: SubDomain.h:61
ChimeraTK::DataDescriptor::FundamentalType::nodata
@ nodata
VariableDistributor.h
TriggeredPollDistributor.h
ChimeraTK::async::detail::SubDomainSubscriptionImplementor::subscribeTo
static boost::shared_ptr< AsyncNDRegisterAccessor< UserType > > subscribeTo(SubDomain< BackendSpecificDataType > &subDomain, RegisterPath name, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Definition: SubDomain.h:247
ChimeraTK::async::SubDomain::_variableDistributor
boost::weak_ptr< VariableDistributor< std::nullptr_t > > _variableDistributor
Definition: SubDomain.h:66
ChimeraTK::async::SubDomain::_parent
boost::shared_ptr< MuxedInterruptDistributor > _parent
Definition: SubDomain.h:67
ChimeraTK::async::detail::SubDomainSubscriptionImplementor
Definition: SubDomain.h:22
ChimeraTK::async::SubDomain::subscribe
boost::shared_ptr< AsyncNDRegisterAccessor< UserType > > subscribe(RegisterPath name, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Definition: SubDomain.h:101
ChimeraTK::async::SubDomain::_domain
boost::shared_ptr< Domain > _domain
Definition: SubDomain.h:68
ChimeraTK::async::SubDomain::_muxedInterruptDistributor
boost::weak_ptr< MuxedInterruptDistributor > _muxedInterruptDistributor
Definition: SubDomain.h:64
ChimeraTK::RegisterPath
Class to store a register path name.
Definition: RegisterPath.h:16
ChimeraTK::async::SubDomain::getBackend
boost::shared_ptr< DeviceBackend > getBackend()
Definition: SubDomain.h:54
ChimeraTK::VersionNumber
Class for generating and holding version numbers without exposing a numeric representation.
Definition: VersionNumber.h:23
ChimeraTK::async::SubDomain::_pollDistributor
boost::weak_ptr< TriggeredPollDistributor > _pollDistributor
Definition: SubDomain.h:65
ChimeraTK::async::SubDomain::_backend
boost::shared_ptr< DeviceBackend > _backend
Definition: SubDomain.h:63
ChimeraTK::AccessModeFlags
Set of AccessMode flags with additional functionality for an easier handling.
Definition: AccessMode.h:48
ChimeraTK::async::VariableDistributor
Definition: VariableDistributor.h:13
ChimeraTK::async::SubDomain::SubDomain
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
ChimeraTK::async::SubDomain::sendException
void sendException(const std::exception_ptr &e)
Definition: SubDomain.h:226
ChimeraTK::async::SubDomain::getAccessorManager
boost::shared_ptr< AsyncAccessorManager > getAccessorManager(std::vector< size_t > const &qualifiedSubDomainId)
Get an AsyncAccessorManager for a specific SubDomain.
Definition: SubDomain.h:111
ChimeraTK::async::SubDomain::getId
std::vector< size_t > getId()
Definition: SubDomain.h:53
ChimeraTK::async::SubDomain::activate
void activate(BackendSpecificDataType, VersionNumber v)
Definition: SubDomain.h:208
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51