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