ChimeraTK-DeviceAccess-DoocsBackend  01.09.02
DoocsBackendNumericRegisterAccessor.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 
6 #include <doocs/EqCall.h>
7 #include <type_traits>
8 
9 #include <ChimeraTK/SupportedUserTypes.h>
10 
11 #include <string>
12 
13 namespace ChimeraTK {
14 
15  /********************************************************************************************************************/
16 
17  template<typename UserType>
19  public:
21 
22  protected:
23  DoocsBackendNumericRegisterAccessor(boost::shared_ptr<DoocsBackend> backend, const std::string& path,
24  const std::string& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags);
25 
26  void doPostRead(TransferType type, bool hasNewData) override;
27 
28  void doPreWrite(TransferType type, VersionNumber) override;
29 
30  // simple helper function to call the correct doocs::EqData::get_...() function to fetch data from dst
31  template<typename T>
32  T dataGet(int index);
33 
34  // Helper function to call a callable for a C++ numeric type corresponding to the type in the doocs::EqData object.
35  // Calling this function for an unsupported data type will throw a runtime error.
36  // Note: This function does not support IFFF, since the data type depends on the index there. Calling this
37  // function with an IFFF raises an assertion.
38  template<typename CALLABLE>
39  void callForDoocsType(const doocs::EqData& data, CALLABLE callable);
40 
41  friend class DoocsBackend;
42  };
43 
44  /********************************************************************************************************************/
45 
46  template<typename UserType>
48  boost::shared_ptr<DoocsBackend> backend, const std::string& path, const std::string& registerPathName,
49  size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
50  : DoocsBackendRegisterAccessor<UserType>(
51  backend, path, registerPathName, numberOfWords, wordOffsetInRegister, flags) {
52  // Check whether data type is supported, just to make sure the backend uses the accessor properly (hence assert,
53  // not exception).
54  // Note: We cannot rely subsequently that the data type remains unchanged, since it might change either dynamically
55  // or by replacing the server (the data type might even come from the cache file).
56  try {
57  // callForDoocsType() will throw a runtime_error if the type is not supported
58  callForDoocsType(this->src, [](auto) {});
59  }
60  catch(ChimeraTK::runtime_error&) {
61  // This is not a real runtime_error here, since this should be already prevented by logic in the backend
62  assert(false);
63  this->shutdown();
64  std::terminate();
65  }
66  }
67 
68  /********************************************************************************************************************/
69 
70  template<typename UserType>
72  this->shutdown();
73  }
74 
75  /********************************************************************************************************************/
76 
77  template<typename UserType>
78  template<typename CALLABLE>
79  void DoocsBackendNumericRegisterAccessor<UserType>::callForDoocsType(const doocs::EqData& data, CALLABLE callable) {
80  switch(data.type()) {
81  case DATA_BOOL:
82  case DATA_A_BOOL:
83  callable(bool());
84  return;
85 
86  case DATA_SHORT:
87  case DATA_A_SHORT:
88  callable(int16_t());
89  return;
90 
91  case DATA_USHORT:
92  case DATA_A_USHORT:
93  callable(uint16_t());
94  return;
95 
96  case DATA_INT:
97  case DATA_A_INT:
98  case DATA_IIII:
99  callable(int32_t());
100  return;
101 
102  case DATA_UINT:
103  case DATA_A_UINT:
104  callable(uint32_t());
105  return;
106 
107  case DATA_LONG:
108  case DATA_A_LONG:
109  callable(int64_t());
110  return;
111 
112  case DATA_ULONG:
113  case DATA_A_ULONG:
114  callable(uint64_t());
115  return;
116 
117  case DATA_FLOAT:
118  case DATA_SPECTRUM:
119  case DATA_GSPECTRUM:
120  case DATA_A_FLOAT:
121  callable(float());
122  return;
123 
124  case DATA_DOUBLE:
125  case DATA_A_DOUBLE:
126  callable(double());
127  return;
128 
129  default:
130  throw ChimeraTK::runtime_error("DoocsBackend: Unexpected DOOCS type found in remote property " +
131  this->ea.show_adr() + ": " + data.type_string() + " is not a numeric type.");
132  }
133  }
134 
135  /********************************************************************************************************************/
136 
137  template<typename UserType>
138  template<typename T>
140  switch(this->dst.type()) {
141  case DATA_BOOL:
142  return this->dst.get_bool();
143 
144  case DATA_A_BOOL:
145  case DATA_SHORT:
146  case DATA_A_SHORT:
147  return this->dst.get_short(index);
148 
149  case DATA_USHORT:
150  case DATA_A_USHORT:
151  return this->dst.get_ushort(index);
152 
153  case DATA_INT:
154  case DATA_A_INT:
155  case DATA_IIII:
156  return this->dst.get_int(index);
157 
158  case DATA_UINT:
159  case DATA_A_UINT:
160  return this->dst.get_uint(index);
161 
162  case DATA_LONG:
163  case DATA_A_LONG:
164  return this->dst.get_long(index);
165 
166  case DATA_ULONG:
167  case DATA_A_ULONG:
168  return this->dst.get_ulong(index);
169 
170  case DATA_FLOAT:
171  case DATA_SPECTRUM:
172  case DATA_GSPECTRUM:
173  case DATA_A_FLOAT:
174  return this->dst.get_float(index);
175 
176  case DATA_DOUBLE:
177  case DATA_A_DOUBLE:
178  default:
179  return this->dst.get_double(index);
180  }
181  }
182 
183  /********************************************************************************************************************/
184 
185  template<>
186  inline void DoocsBackendNumericRegisterAccessor<ChimeraTK::Void>::doPostRead(TransferType type, bool hasNewData) {
188  }
189 
190  /********************************************************************************************************************/
191 
192  template<typename UserType>
193  void DoocsBackendNumericRegisterAccessor<UserType>::doPostRead(TransferType type, bool hasNewData) {
195  if(!hasNewData) return;
196 
197  // special workaround for D_spectrum: Data type will be DATA_NULL if error is set to "stale data"
198  if(this->dst.type() == DATA_NULL) {
199  // Unfortunately there is no way to get to the data at this point, so fill the buffer with zeros. The data
200  // validity has been set to invalid in this case already by the
201  // DoocsBackendRegisterAccessor<UserType>::doPostRead() call above.
202  std::fill(this->accessChannel(0).begin(), this->accessChannel(0).end(), UserType());
203  return;
204  }
205 
206  // verify array length
207  if(size_t(this->dst.length()) < this->nElements + this->elementOffset) {
208  throw ChimeraTK::runtime_error("DoocsBackend: Unexpected array length found in remote property " +
209  this->ea.show_adr() + ": " + std::to_string(this->dst.length()) + " is shorter than the expected " +
210  std::to_string(this->nElements + this->elementOffset));
211  }
212 
213  // define lambda used below for optimisation
214  auto copyFromSourcePointer = [&](const auto* sourcePointer) {
215  using SourceType = std::remove_const_t<std::remove_reference_t<decltype(*sourcePointer)>>;
216  assert(sourcePointer);
217 
218  sourcePointer += this->elementOffset;
219  if constexpr(std::is_same<UserType, SourceType>::value) {
220  // raw and target data type match, do a memcopy
221  memcpy(this->buffer_2D[0].data(), sourcePointer, this->nElements * sizeof(SourceType));
222  }
223  else {
224  std::transform(sourcePointer, sourcePointer + this->nElements, this->buffer_2D[0].begin(),
225  [](const SourceType& v) { return ChimeraTK::numericToUserType<UserType>(v); });
226  }
227  };
228 
229  // optimise depending on type
230  switch(this->dst.type()) {
231  case DATA_SPECTRUM:
232  case DATA_A_FLOAT: {
233  copyFromSourcePointer(this->dst.get_float_array());
234  return;
235  }
236  case DATA_A_DOUBLE: {
237  copyFromSourcePointer(this->dst.get_double_array());
238  return;
239  }
240  case DATA_A_INT: {
241  copyFromSourcePointer(this->dst.get_int_array());
242  return;
243  }
244  case DATA_A_LONG: {
245  copyFromSourcePointer(this->dst.get_long_array());
246  return;
247  }
248  case DATA_A_BOOL:
249  case DATA_A_SHORT: {
250  copyFromSourcePointer(this->dst.get_short_array());
251  return;
252  }
253  default:
254  // inefficient copying via single element access for other data types (including scalars)
255  callForDoocsType(this->dst, [&](auto t) {
256  using T = decltype(t);
257  for(size_t i = 0; i < this->nElements; i++) {
258  this->buffer_2D[0][i] = ChimeraTK::numericToUserType<UserType>(dataGet<T>(i + this->elementOffset));
259  }
260  });
261  }
262  }
263 
264  /********************************************************************************************************************/
265 
266  template<>
268  TransferType type, VersionNumber version) {
270 
271  src.set(0);
272  }
273 
274  /********************************************************************************************************************/
275 
276  template<typename UserType>
277  void DoocsBackendNumericRegisterAccessor<UserType>::doPreWrite(TransferType type, VersionNumber version) {
279 
280  // implement read-modify-write
281  if(this->isPartial) {
282  this->doReadTransferSynchronously();
283  this->src = this->dst;
284  // make sure the array is long enough, as the remote server might have changed the length on its side
285  if(size_t(this->src.length()) < this->nElements + this->elementOffset) {
286  this->src.length(this->nElements + this->elementOffset);
287  }
288  }
289 
290  // define lambda used below for optimisation
291  auto copyToTargetPointer = [&](auto* targetPointer) {
292  using TargetType = std::remove_reference_t<decltype(*targetPointer)>;
293  assert(targetPointer);
294 
295  targetPointer += this->elementOffset;
296  if constexpr(std::is_same<UserType, TargetType>::value) {
297  // raw and target data type match, do a memcopy
298  memcpy(targetPointer, this->buffer_2D[0].data(), this->nElements * sizeof(TargetType));
299  }
300  else {
301  std::transform(this->buffer_2D[0].begin(), this->buffer_2D[0].end(), targetPointer,
302  [](UserType v) { return ChimeraTK::userTypeToNumeric<TargetType>(v); });
303  }
304  };
305 
306  // optimise depending on type
307  switch(this->src.type()) {
308  case DATA_SPECTRUM:
309  case DATA_A_FLOAT: {
310  copyToTargetPointer(this->src.get_float_array());
311  return;
312  }
313  case DATA_A_DOUBLE: {
314  copyToTargetPointer(this->src.get_double_array());
315  return;
316  }
317  case DATA_A_INT: {
318  copyToTargetPointer(this->src.get_int_array());
319  return;
320  }
321  case DATA_A_LONG: {
322  copyToTargetPointer(this->src.get_long_array());
323  return;
324  }
325  case DATA_A_BOOL:
326  case DATA_A_SHORT: {
327  copyToTargetPointer(this->src.get_short_array());
328  return;
329  }
330  default:
331  // inefficient copying via single element access for other data types (including scalars)
332  callForDoocsType(this->src, [&](auto t) {
333  using T = decltype(t);
334  if(this->src.array_length() == 0) {
335  // scalar (IFFF, IIII and string are handled in different accessors)
336  this->src.set(ChimeraTK::userTypeToNumeric<T>(this->buffer_2D[0][0]));
337  }
338  else {
339  for(size_t i = 0; i < this->nElements; i++) {
340  this->src.set(ChimeraTK::userTypeToNumeric<T>(this->buffer_2D[0][i]), int(i + this->elementOffset));
341  }
342  }
343  });
344  }
345  }
346 
347  /********************************************************************************************************************/
348 
349 } // namespace ChimeraTK
ChimeraTK::DoocsBackendRegisterAccessor
Definition: DoocsBackendRegisterAccessor.h:75
ChimeraTK::DoocsBackendNumericRegisterAccessor::dataGet
T dataGet(int index)
Definition: DoocsBackendNumericRegisterAccessor.h:139
ChimeraTK::DoocsBackendNumericRegisterAccessor::callForDoocsType
void callForDoocsType(const doocs::EqData &data, CALLABLE callable)
Definition: DoocsBackendNumericRegisterAccessor.h:79
ChimeraTK::DoocsBackendNumericRegisterAccessor::doPostRead
void doPostRead(TransferType type, bool hasNewData) override
Definition: DoocsBackendNumericRegisterAccessor.h:193
ChimeraTK::DoocsBackendNumericRegisterAccessor
Definition: DoocsBackendNumericRegisterAccessor.h:18
ChimeraTK::DoocsBackendRegisterAccessor::doPostRead
void doPostRead(TransferType, bool hasNewData) override
Definition: DoocsBackendRegisterAccessor.h:108
ChimeraTK::DoocsBackendRegisterAccessorBase::src
doocs::EqData src
DOOCS data structures.
Definition: DoocsBackendRegisterAccessor.h:37
ChimeraTK::DoocsBackendRegisterAccessor::doPreWrite
void doPreWrite(TransferType, VersionNumber) override
Definition: DoocsBackendRegisterAccessor.h:103
ChimeraTK::DoocsBackendRegisterAccessor::shutdown
void shutdown()
All implementations must call this function in their destructor.
Definition: DoocsBackendRegisterAccessor.h:84
ChimeraTK::DoocsBackendNumericRegisterAccessor::doPreWrite
void doPreWrite(TransferType type, VersionNumber) override
Definition: DoocsBackendNumericRegisterAccessor.h:277
ChimeraTK::DoocsBackendNumericRegisterAccessor::DoocsBackendNumericRegisterAccessor
DoocsBackendNumericRegisterAccessor(boost::shared_ptr< DoocsBackend > backend, const std::string &path, const std::string &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Definition: DoocsBackendNumericRegisterAccessor.h:47
ChimeraTK::DoocsBackendNumericRegisterAccessor::~DoocsBackendNumericRegisterAccessor
~DoocsBackendNumericRegisterAccessor() override
Definition: DoocsBackendNumericRegisterAccessor.h:71
ChimeraTK
Definition: spec_DoocsBackend.dox:2
ChimeraTK::DoocsBackend
Backend to access DOOCS control system servers.
Definition: DoocsBackend.h:56
DoocsBackendRegisterAccessor.h