ChimeraTK-DeviceAccess-DoocsBackend 01.12.00
Loading...
Searching...
No Matches
testUnifiedBackendTest.cpp
Go to the documentation of this file.
1/*
2 * testUnifiedBackendTest.cpp
3 *
4 * Created on: Jun 03, 2020
5 * Author: Martin Hierholzer
6 */
7
8#include <unistd.h>
9
10#include <cstdlib>
11#include <iostream>
12#include <random>
13#include <thread>
14
15#define BOOST_TEST_MODULE testUnifiedBackendTest
16#include "eq_dummy.h"
17
18#include <ChimeraTK/Device.h>
19#include <ChimeraTK/TransferGroup.h>
20#include <ChimeraTK/UnifiedBackendTest.h>
21
22#include <doocs-server-test-helper/doocsServerTestHelper.h>
23#include <doocs-server-test-helper/ThreadedDoocsServer.h>
24#include <doocs/EqCall.h>
25
26#include <boost/filesystem.hpp>
27#include <boost/test/included/unit_test.hpp>
28
29#include <fstream>
30
31using namespace boost::unit_test_framework;
32using namespace ChimeraTK;
33
34/**********************************************************************************************************************/
35
36static std::atomic<bool> zmqStartup_gotData{false};
37
38class DoocsLauncher : public ThreadedDoocsServer {
39 public:
41 : ThreadedDoocsServer("testUnifiedBackendTest.conf", boost::unit_test::framework::master_test_suite().argc,
42 boost::unit_test::framework::master_test_suite().argv, eq_dummy::createServer()) {
43 // set CDDs for the two doocs addresses used in the test
44 rpc_no = rpcNo();
45 DoocsServer = "doocs:doocs://localhost:" + rpcNo() + "/F/D";
46
47 // wait until server has started (both the update thread and the rpc thread)
48 doocs::EqCall eq;
49 doocs::EqAdr ea;
50 doocs::EqData src, dst;
51 ea.adr("doocs://localhost:" + rpcNo() + "/F/D/MYDUMMY/SOME_ZMQINT");
52 while(eq.get(&ea, &src, &dst)) usleep(100000);
53
54 // wait until ZeroMQ is working
55 dmsg_start();
56 dmsg_t tag;
57 std::cout << "ZeroMQ Subscribe" << std::endl;
58 [[maybe_unused]] int err = dmsg_attach(
59 &ea, &dst, nullptr,
60 [](void*, doocs::EqData* data, dmsg_info_t*) {
61 if(!data->error()) zmqStartup_gotData = true;
62 },
63 &tag);
64 assert(!err);
65 std::cout << "ZeroMQ wait" << std::endl;
66 [[maybe_unused]] auto location = dynamic_cast<eq_dummy*>(find_device("MYDUMMY"));
67 assert(location != nullptr);
68 while(!zmqStartup_gotData) {
69 usleep(100000);
70 DoocsServerTestHelper::runUpdate();
71 }
72 std::cout << "OK!" << std::endl;
73 dmsg_detach(&ea, nullptr);
74 }
75
76 static std::string rpc_no;
77 static std::string DoocsServer;
78};
79
80std::string DoocsLauncher::rpc_no;
82
83static eq_dummy* location{nullptr};
84
85static size_t mpn = 10000;
86static size_t seconds = 0;
87static size_t microseconds = 0;
88
90
91/**********************************************************************************************************************/
92
93template<typename DERIVED>
95 DERIVED* derived{static_cast<DERIVED*>(this)};
96
97 bool isWriteable() { return true; }
98 bool isReadable() { return true; }
99 ChimeraTK::AccessModeFlags supportedFlags() { return {}; }
100 size_t nChannels() { return 1; }
101 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
102 size_t nRuntimeErrorCases() { return 1; }
103 typedef std::nullptr_t rawUserType;
104
105 static constexpr auto capabilities = TestCapabilities<>()
106 .disableForceDataLossWrite()
107 .disableAsyncReadInconsistency()
108 .disableSwitchReadOnly()
109 .disableSwitchWriteOnly();
110
111 void setForceRuntimeError(bool enable, size_t) {
112 if(enable) {
113 location->lock();
114 }
115 else {
116 location->unlock();
117 }
118 }
119
120 void updateStamp() {
121 ++mpn;
122 microseconds += 100000;
123 if(microseconds > 1000000) {
124 microseconds -= 1000000;
125 ++seconds;
126 }
127 derived->prop.set_tmstmp(seconds, microseconds);
128 derived->prop.set_mpnum(mpn);
129 }
130};
131
132/**********************************************************************************************************************/
133
134template<typename DERIVED>
137 using AllRegisterDefaults<DERIVED>::derived;
138 size_t nElementsPerChannel() { return 1; }
139
140 template<typename UserType>
141 std::vector<std::vector<UserType>> generateValue() {
142 return {{ChimeraTK::numericToUserType<UserType>(static_cast<typename DERIVED::minimumUserType>(
143 derived->template getRemoteValue<typename DERIVED::minimumUserType>()[0][0] + derived->increment))}};
144 }
145
146 template<typename UserType>
147 std::vector<std::vector<UserType>> getRemoteValue() {
148 std::lock_guard<EqFct> lk(*location);
149 auto v = ChimeraTK::numericToUserType<UserType>(derived->prop.value());
150 return {{v}};
151 }
152
154 auto v = derived->template generateValue<typename DERIVED::minimumUserType>()[0][0];
155 std::lock_guard<EqFct> lk(*location);
156 derived->prop.set_value(v);
157 this->updateStamp();
158 }
159};
160
161/**********************************************************************************************************************/
162
163template<typename DERIVED>
166 using AllRegisterDefaults<DERIVED>::derived;
167
168 template<typename UserType>
169 std::vector<std::vector<UserType>> generateValue() {
170 auto curval = derived->template getRemoteValue<typename DERIVED::minimumUserType>()[0];
171 std::vector<UserType> val;
172 for(size_t i = 0; i < derived->nElementsPerChannel(); ++i) {
173 val.push_back(ChimeraTK::numericToUserType<UserType>(curval[i] + (i + 1) * derived->increment));
174 }
175 return {val};
176 }
177
178 template<typename UserType>
179 std::vector<std::vector<UserType>> getRemoteValue() {
180 std::vector<UserType> val;
181 std::lock_guard<EqFct> lk(*location);
182 for(size_t i = 0; i < derived->nElementsPerChannel(); ++i) {
183 val.push_back(ChimeraTK::numericToUserType<UserType>(derived->prop.value(i)));
184 }
185 return {val};
186 }
187
189 auto val = derived->template generateValue<typename DERIVED::minimumUserType>()[0];
190 std::lock_guard<EqFct> lk(*location);
191 for(size_t i = 0; i < derived->nElementsPerChannel(); ++i) {
192 derived->prop.set_value(val[i], i);
193 }
194 this->updateStamp();
195 }
196};
197
198/**********************************************************************************************************************/
199
200struct RegSomeInt : ScalarDefaults<RegSomeInt> {
201 std::string path() { return "MYDUMMY/SOME_INT"; }
202 D_int& prop{location->prop_someInt};
203 typedef int32_t minimumUserType;
204 int32_t increment{3};
205
206 static constexpr auto capabilities = ScalarDefaults<RegSomeInt>::capabilities.enableSwitchReadOnly();
207
208 void switchReadOnly(bool enable) {
209 if(enable) {
210 prop.set_ro_access();
211 }
212 else {
213 prop.set_rw_access();
214 }
215 }
216};
217
218/**********************************************************************************************************************/
219
220struct RegSomeRoInt : ScalarDefaults<RegSomeInt> {
221 bool isWriteable() { return false; }
222 std::string path() { return "MYDUMMY/SOME_RO_INT"; }
223 D_int& prop{location->prop_someReadonlyInt};
224 typedef int32_t minimumUserType;
225 int32_t increment{7};
226 // the catalogue cannot be filled correctly because we don't know if the register is readable or not when filling the catalogue
227 static constexpr auto capabilities = ScalarDefaults<RegSomeInt>::capabilities.disableTestCatalogue();
228};
229
230/**********************************************************************************************************************/
231
232struct RegSomeZmqInt : ScalarDefaults<RegSomeZmqInt> {
233 AccessModeFlags supportedFlags() { return {AccessMode::wait_for_new_data}; }
234 std::string path() { return "MYDUMMY/SOME_ZMQINT"; }
235 D_int& prop{location->prop_someZMQInt};
236 typedef int32_t minimumUserType;
237 int32_t increment{51};
238
239 static constexpr auto capabilities = ScalarDefaults<RegSomeZmqInt>::capabilities.enableAsyncReadInconsistency();
240
243
244 std::lock_guard<EqFct> lk(*location);
245 dmsg_info_t info;
246 memset(&info, 0, sizeof(info));
247 info.sec = seconds;
248 info.usec = microseconds;
249 info.ident = mpn;
250 prop.send(&info);
251 usleep(10000); // FIXME: DOOCS-ZeroMQ seems to lose data when sending too fast...
252 }
253
255 // Change value without sending it via ZeroMQ
257 }
258};
259
260/**********************************************************************************************************************/
261
262struct RegSomeFloat : ScalarDefaults<RegSomeFloat> {
263 std::string path() { return "MYDUMMY/SOME_FLOAT"; }
264 D_float& prop{location->prop_someFloat};
265 typedef float minimumUserType;
266 float increment{std::exp(1.F)};
267};
268
269/**********************************************************************************************************************/
270
271struct RegSomeDouble : ScalarDefaults<RegSomeDouble> {
272 std::string path() { return "MYDUMMY/SOME_DOUBLE"; }
273 D_double& prop{location->prop_someDouble};
274 typedef double minimumUserType;
275 double increment{std::exp(1.5)};
276};
277
278/**********************************************************************************************************************/
279
280struct RegSomeString : ScalarDefaults<RegSomeString> {
281 std::string path() { return "MYDUMMY/SOME_STRING"; }
282 D_string& prop{location->prop_someString};
283 typedef std::string minimumUserType;
284
285 template<typename UserType>
286 std::vector<std::vector<UserType>> generateValue() {
287 assert(false); // See specialisation below
288 }
289
290 template<typename UserType>
291 std::vector<std::vector<UserType>> getRemoteValue() {
292 std::vector<UserType> val;
293 std::lock_guard<EqFct> lk(*location);
294 val.push_back(prop.value());
295 return {val};
296 }
297
298 size_t someValue{42};
299};
300
301template<>
302std::vector<std::vector<std::string>> RegSomeString::generateValue<std::string>() {
303 ++someValue;
304 return {{"This is a string: " + std::to_string(someValue)}};
305}
306
307struct RegSomeBit : ScalarDefaults<RegSomeBit> {
308 std::string path() { return "MYDUMMY/SOME_BIT"; }
309 D_bit& prop{location->prop_someBit};
310 typedef ChimeraTK::Boolean minimumUserType;
311 uint8_t increment{0}; // unused
312
313 template<typename UserType>
314 std::vector<std::vector<UserType>> generateValue() {
315 return {{!this->getRemoteValue<minimumUserType>()[0][0]}};
316 }
317};
318
319/**********************************************************************************************************************/
320
321struct RegSomeIntArray : ArrayDefaults<RegSomeIntArray> {
322 std::string path() { return "MYDUMMY/SOME_INT_ARRAY"; }
323 size_t nElementsPerChannel() { return 42; }
324 D_intarray& prop{location->prop_someIntArray};
325 typedef int32_t minimumUserType;
326 int32_t increment{11};
327};
328
329/**********************************************************************************************************************/
330
331struct RegSomeShortArray : ArrayDefaults<RegSomeShortArray> {
332 std::string path() { return "MYDUMMY/SOME_SHORT_ARRAY"; }
333 size_t nElementsPerChannel() { return 5; }
334 D_shortarray& prop{location->prop_someShortArray};
335 typedef int16_t minimumUserType;
336 int16_t increment{17};
337};
338
339/**********************************************************************************************************************/
340
341struct RegSomeLongArray : ArrayDefaults<RegSomeLongArray> {
342 std::string path() { return "MYDUMMY/SOME_LONG_ARRAY"; }
343 size_t nElementsPerChannel() { return 5; }
344 D_longarray& prop{location->prop_someLongArray};
345 typedef int64_t minimumUserType;
346 int64_t increment{23};
347};
348
349/**********************************************************************************************************************/
350
351struct RegSomeFloatArray : ArrayDefaults<RegSomeFloatArray> {
352 std::string path() { return "MYDUMMY/SOME_FLOAT_ARRAY"; }
353 size_t nElementsPerChannel() { return 5; }
354 D_floatarray& prop{location->prop_someFloatArray};
355 typedef float minimumUserType;
356 float increment{std::exp(5.F)};
357};
358
359/**********************************************************************************************************************/
360
361struct RegSomeDoubleArray : ArrayDefaults<RegSomeDoubleArray> {
362 std::string path() { return "MYDUMMY/SOME_DOUBLE_ARRAY"; }
363 size_t nElementsPerChannel() { return 5; }
364 D_doublearray& prop{location->prop_someDoubleArray};
365 typedef double minimumUserType;
366 double increment{std::exp(7.)};
367};
368
369/**********************************************************************************************************************/
370
371struct RegSomeSpectrum : ArrayDefaults<RegSomeSpectrum> {
372 std::string path() { return "MYDUMMY/SOME_SPECTRUM"; }
373 size_t nElementsPerChannel() { return 100; }
374 D_spectrum& prop{location->prop_someSpectrum};
375 typedef float minimumUserType;
376 float increment{std::exp(11.F)};
377
379 auto val = generateValue<minimumUserType>()[0];
380 std::lock_guard<EqFct> lk(*location);
381 prop.current_buffer(0);
382 for(size_t i = 0; i < nElementsPerChannel(); ++i) {
383 prop.fill_spectrum(i, val[i]);
384 }
385 this->updateStamp();
386 }
387
388 template<typename UserType>
389 std::vector<std::vector<UserType>> getRemoteValue() {
390 std::vector<UserType> val;
391 std::lock_guard<EqFct> lk(*location);
392 for(size_t i = 0; i < nElementsPerChannel(); ++i) {
393 val.push_back(prop.read_spectrum(i));
394 }
395 return {val};
396 }
397
398 void updateStamp() {
399 ++mpn;
400 microseconds += 100000;
401 if(microseconds > 1000000) {
402 microseconds -= 100000;
403 ++seconds;
404 }
405 derived->prop.set_tmstmp(seconds, microseconds, 0);
406 derived->prop.set_mpnum(mpn);
407 }
408};
409
410/**********************************************************************************************************************/
411
412struct RegSomeIiii : ArrayDefaults<RegSomeIiii> {
413 std::string path() { return "MYDUMMY/SOME_IIII"; }
414 size_t nElementsPerChannel() { return 4; }
415 D_iiii& prop{location->prop_someIIII};
416 typedef int32_t minimumUserType;
417 int32_t increment{13};
418
420 auto val = generateValue<minimumUserType>()[0];
421 std::lock_guard<EqFct> lk(*location);
422 prop.set_value(val[0], val[1], val[2], val[3]);
423 this->updateStamp();
424 }
425
426 template<typename UserType>
427 std::vector<std::vector<UserType>> getRemoteValue() {
428 std::lock_guard<EqFct> lk(*location);
429 auto val = prop.value();
430 return {{ChimeraTK::numericToUserType<UserType>(val->i1_data), ChimeraTK::numericToUserType<UserType>(val->i2_data),
431 ChimeraTK::numericToUserType<UserType>(val->i3_data), ChimeraTK::numericToUserType<UserType>(val->i4_data)}};
432 }
433};
434
435/**********************************************************************************************************************/
436
437struct RegSomeIfff_I : ScalarDefaults<RegSomeIfff_I> {
438 std::string path() { return "MYDUMMY/SOME_IFFF/I"; }
439 D_ifff& prop{location->prop_someIFFF};
440 typedef int32_t minimumUserType;
441 int32_t increment{23};
442
444 auto v = generateValue<minimumUserType>()[0][0];
445 std::lock_guard<EqFct> lk(*location);
446 auto curval = prop.value();
447 prop.set_value(v, curval->f1_data, curval->f2_data, curval->f3_data);
448 this->updateStamp();
449 }
450
451 template<typename UserType>
452 std::vector<std::vector<UserType>> getRemoteValue() {
453 std::lock_guard<EqFct> lk(*location);
454 return {{ChimeraTK::numericToUserType<UserType>(prop.value()->i1_data)}};
455 }
456};
457
458/**********************************************************************************************************************/
459
460struct RegSomeIfff_F1 : ScalarDefaults<RegSomeIfff_F1> {
461 std::string path() { return "MYDUMMY/SOME_IFFF/F1"; }
462 D_ifff& prop{location->prop_someIFFF};
463 typedef float minimumUserType;
464 float increment{std::exp(3.14F)};
465
467 auto v = generateValue<minimumUserType>()[0][0];
468 std::lock_guard<EqFct> lk(*location);
469 auto curval = prop.value();
470 prop.set_value(curval->i1_data, v, curval->f2_data, curval->f3_data);
471 this->updateStamp();
472 }
473
474 template<typename UserType>
475 std::vector<std::vector<UserType>> getRemoteValue() {
476 std::lock_guard<EqFct> lk(*location);
477 return {{ChimeraTK::numericToUserType<UserType>(prop.value()->f1_data)}};
478 }
479};
480
481/**********************************************************************************************************************/
482
483struct RegSomeIfff_F2 : ScalarDefaults<RegSomeIfff_F2> {
484 std::string path() { return "MYDUMMY/SOME_IFFF/F2"; }
485 D_ifff& prop{location->prop_someIFFF};
486 typedef float minimumUserType;
487 float increment{std::exp(1.23F)};
488
490 auto v = generateValue<minimumUserType>()[0][0];
491 std::lock_guard<EqFct> lk(*location);
492 auto curval = prop.value();
493 prop.set_value(curval->i1_data, curval->f1_data, v, curval->f3_data);
494 this->updateStamp();
495 }
496
497 template<typename UserType>
498 std::vector<std::vector<UserType>> getRemoteValue() {
499 std::lock_guard<EqFct> lk(*location);
500 return {{ChimeraTK::numericToUserType<UserType>(prop.value()->f2_data)}};
501 }
502};
503
504/**********************************************************************************************************************/
505
506struct RegSomeIfff_F3 : ScalarDefaults<RegSomeIfff_F3> {
507 std::string path() { return "MYDUMMY/SOME_IFFF/F3"; }
508 D_ifff& prop{location->prop_someIFFF};
509 typedef float minimumUserType;
510 float increment{std::exp(2.34F)};
511
513 auto v = generateValue<minimumUserType>()[0][0];
514 std::lock_guard<EqFct> lk(*location);
515 auto curval = prop.value();
516 prop.set_value(curval->i1_data, curval->f1_data, curval->f2_data, v);
517 this->updateStamp();
518 }
519
520 template<typename UserType>
521 std::vector<std::vector<UserType>> getRemoteValue() {
522 std::lock_guard<EqFct> lk(*location);
523 return {{ChimeraTK::numericToUserType<UserType>(prop.value()->f3_data)}};
524 }
525};
526
527/**********************************************************************************************************************/
528
529BOOST_AUTO_TEST_CASE(unifiedBackendTest) {
530 location = dynamic_cast<eq_dummy*>(find_device("MYDUMMY"));
531 assert(location != nullptr);
532
533 doocs::Timestamp timestamp = get_global_timestamp();
534 auto sinceEpoch = timestamp.get_seconds_and_microseconds_since_epoch();
535 seconds = sinceEpoch.seconds + 1;
536
537 doocs::zmq_set_subscription_timeout(10); // reduce timout to make test faster
538
539 auto ubt = ChimeraTK::UnifiedBackendTest<>()
540 .addRegister<RegSomeInt>()
541 .addRegister<RegSomeRoInt>()
542 .addRegister<RegSomeZmqInt>()
543 .addRegister<RegSomeFloat>()
544 .addRegister<RegSomeDouble>()
545 .addRegister<RegSomeString>()
546 .addRegister<RegSomeBit>()
547 .addRegister<RegSomeIntArray>()
548 .addRegister<RegSomeShortArray>()
549 .addRegister<RegSomeLongArray>()
550 .addRegister<RegSomeFloatArray>()
551 .addRegister<RegSomeDoubleArray>()
552 .addRegister<RegSomeSpectrum>()
553 .addRegister<RegSomeIiii>()
554 .addRegister<RegSomeIfff_I>()
555 .addRegister<RegSomeIfff_F1>()
556 .addRegister<RegSomeIfff_F2>()
557 .addRegister<RegSomeIfff_F3>();
558
559 ubt.runTests("(" + DoocsLauncher::DoocsServer + ")", "(" + DoocsLauncher::DoocsServer + "?unused=parameter)");
560}
561
562/**********************************************************************************************************************/
static std::string DoocsServer
static std::string rpc_no
D_double prop_someDouble
Definition eq_dummy.h:16
D_floatarray prop_someFloatArray
Definition eq_dummy.h:24
D_string prop_someString
Definition eq_dummy.h:17
D_longarray prop_someLongArray
Definition eq_dummy.h:23
D_doublearray prop_someDoubleArray
Definition eq_dummy.h:25
D_ifff prop_someIFFF
Definition eq_dummy.h:29
D_int prop_someInt
Definition eq_dummy.h:13
D_spectrum prop_someSpectrum
Definition eq_dummy.h:27
D_bit prop_someBit
Definition eq_dummy.h:19
D_shortarray prop_someShortArray
Definition eq_dummy.h:22
D_iiii prop_someIIII
Definition eq_dummy.h:28
D_intarray prop_someIntArray
Definition eq_dummy.h:21
D_int prop_someZMQInt
Definition eq_dummy.h:33
D_float prop_someFloat
Definition eq_dummy.h:15
D_int prop_someReadonlyInt
Definition eq_dummy.h:14
static constexpr auto capabilities
void setForceRuntimeError(bool enable, size_t)
ChimeraTK::AccessModeFlags supportedFlags()
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > generateValue()
std::vector< std::vector< UserType > > generateValue()
ChimeraTK::Boolean minimumUserType
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > getRemoteValue()
void switchReadOnly(bool enable)
static constexpr auto capabilities
static constexpr auto capabilities
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > generateValue()
AccessModeFlags supportedFlags()
static constexpr auto capabilities
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > generateValue()
BOOST_GLOBAL_FIXTURE(DoocsLauncher)
BOOST_AUTO_TEST_CASE(unifiedBackendTest)