ChimeraTK-DeviceAccess  03.18.00
testScalarRegisterAccessor.cpp
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 
4 #define BOOST_TEST_DYN_LINK
5 #define BOOST_TEST_MODULE ScalarRegisterAccessorTest
6 #include <boost/test/unit_test.hpp>
7 using namespace boost::unit_test_framework;
8 
9 #include "accessPrivateData.h"
10 #include "Device.h"
11 #include "DummyBackend.h"
12 #include "DummyRegisterAccessor.h"
13 #include "ScalarRegisterAccessor.h"
14 #include "WriteCountingBackend.h"
15 #include <unordered_map>
16 
17 #include <boost/bind/bind.hpp>
18 #include <boost/function.hpp>
19 #include <boost/lambda/lambda.hpp>
20 #include <boost/make_shared.hpp>
21 
22 #include <algorithm>
23 #include <math.h>
24 
25 using namespace boost::unit_test_framework;
26 using namespace ChimeraTK;
27 
28 /**********************************************************************************************************************/
29 
30 BOOST_AUTO_TEST_CASE(testCreation) {
31  setDMapFilePath("dummies.dmap");
32  std::cout << "testCreation" << std::endl;
33 
34  Device device;
35  device.open("DUMMYD2");
36  boost::shared_ptr<DummyBackend> backend =
37  boost::dynamic_pointer_cast<DummyBackend>(BackendFactory::getInstance().createBackend("DUMMYD2"));
38  BOOST_CHECK(backend != NULL);
39 
40  // obtain register accessor in disconnected state
41  ScalarRegisterAccessor<int> intRegisterDisconnected;
42  BOOST_CHECK(intRegisterDisconnected.isInitialised() == false);
43  intRegisterDisconnected.replace(device.getScalarRegisterAccessor<int>("APP0/WORD_STATUS"));
44  BOOST_CHECK(intRegisterDisconnected.isInitialised() == true);
45 
46  // obtain register accessor with integral type
47  ScalarRegisterAccessor<int> intRegister = device.getScalarRegisterAccessor<int>("APP0/WORD_STATUS");
48  BOOST_CHECK(intRegister.isInitialised() == true);
49 
50  device.close();
51 }
52 
53 /**********************************************************************************************************************/
54 
55 BOOST_AUTO_TEST_CASE(testIntRegisterAccessor) {
56  setDMapFilePath("dummies.dmap");
57  std::cout << "testRegisterAccessor" << std::endl;
58 
59  Device device;
60  device.open("DUMMYD2");
61  boost::shared_ptr<DummyBackend> backend =
62  boost::dynamic_pointer_cast<DummyBackend>(BackendFactory::getInstance().createBackend("DUMMYD2"));
63  BOOST_CHECK(backend != NULL);
64 
65  // obtain register accessor with integral type
66  ScalarRegisterAccessor<int> accessor = device.getScalarRegisterAccessor<int>("APP0/WORD_STATUS");
67  BOOST_CHECK(accessor.isReadOnly() == false);
68  BOOST_CHECK(accessor.isReadable());
69  BOOST_CHECK(accessor.isWriteable());
70 
71  // dummy register accessor for comparison
72  DummyRegisterAccessor<int> dummy(backend.get(), "APP0", "WORD_STATUS");
73 
74  // test type conversion etc. for reading
75  dummy = 5;
76  accessor.read();
77  BOOST_CHECK(accessor == 5);
78  BOOST_CHECK(int(accessor) == 5);
79  BOOST_CHECK(2 * accessor == 10);
80  BOOST_CHECK(accessor + 2 == 7);
81  dummy = -654;
82  BOOST_CHECK(accessor == 5);
83  accessor.read();
84  BOOST_CHECK(accessor == -654);
85 
86  // test assignment etc. for writing
87  accessor = -666;
88  accessor.write();
89  BOOST_CHECK(dummy == -666);
90  accessor = 222;
91  accessor.write();
92  BOOST_CHECK(dummy == 222);
93 
94  // test pre-increment operator
95  ScalarRegisterAccessor<int> copy = ++accessor;
96 
97  BOOST_CHECK(accessor == 223);
98  BOOST_CHECK(copy == 223);
99  BOOST_CHECK(dummy == 222);
100  accessor.write();
101  BOOST_CHECK(dummy == 223);
102  copy = 3;
103  BOOST_CHECK(accessor == 3);
104  copy.write();
105  BOOST_CHECK(dummy == 3);
106 
107  // test pre-decrement operator
108  copy.replace(--accessor);
109 
110  BOOST_CHECK(accessor == 2);
111  BOOST_CHECK(copy == 2);
112  BOOST_CHECK(dummy == 3);
113  accessor.write();
114  BOOST_CHECK(dummy == 2);
115  copy = 42;
116  BOOST_CHECK(accessor == 42);
117  copy.write();
118  BOOST_CHECK(dummy == 42);
119 
120  // test post-increment operator
121  int oldValue = accessor++;
122 
123  BOOST_CHECK(accessor == 43);
124  BOOST_CHECK(copy == 43);
125  BOOST_CHECK(oldValue == 42);
126  BOOST_CHECK(dummy == 42);
127  accessor.write();
128  BOOST_CHECK(dummy == 43);
129 
130  // test post-decrement operator
131  accessor = 120;
132  oldValue = accessor--;
133 
134  BOOST_CHECK(accessor == 119);
135  BOOST_CHECK(copy == 119);
136  BOOST_CHECK(oldValue == 120);
137  BOOST_CHECK(dummy == 43);
138  accessor.write();
139  BOOST_CHECK(dummy == 119);
140 
141  // test readAndGet
142  dummy = 470;
143  BOOST_CHECK(accessor.readAndGet() == 470);
144 
145  // test setAndWrite
146  accessor.setAndWrite(4711);
147  BOOST_CHECK(dummy == 4711);
148 
149  // test correct version number handling
150  VersionNumber someVersionNumber = VersionNumber();
151  accessor.setAndWrite(815, someVersionNumber);
152  BOOST_CHECK(accessor.getVersionNumber() == someVersionNumber);
153 
154  // test correct version number handling with default values
155  VersionNumber before = VersionNumber();
156  accessor.setAndWrite(77);
157  VersionNumber after = VersionNumber();
158  BOOST_CHECK(accessor.getVersionNumber() > before);
159  BOOST_CHECK(accessor.getVersionNumber() < after);
160 
161  device.close();
162 }
163 
164 /**********************************************************************************************************************/
165 
166 BOOST_AUTO_TEST_CASE(testFloatRegisterAccessor) {
167  setDMapFilePath("dummies.dmap");
168  std::cout << "testFloatRegisterAccessor" << std::endl;
169 
170  Device device;
171  device.open("DUMMYD2");
172  boost::shared_ptr<DummyBackend> backend =
173  boost::dynamic_pointer_cast<DummyBackend>(BackendFactory::getInstance().createBackend("DUMMYD2"));
174  BOOST_CHECK(backend != NULL);
175 
176  // obtain register accessor with integral type
177  ScalarRegisterAccessor<float> accessor = device.getScalarRegisterAccessor<float>("MODULE1/WORD_USER2");
178 
179  // dummy register accessor for comparison
180  DummyRegisterAccessor<float> dummy(backend.get(), "MODULE1", "WORD_USER2");
181 
182  // test type conversion etc. for reading
183  dummy = 5.3;
184  float requiredVal = dummy;
185  BOOST_CHECK_CLOSE(requiredVal, 5.3, 1);
186 
187  accessor.read();
188  float val = accessor; // BOOST_CHECK_CLOSE requires implicit conversion in
189  // both directions, so we must help us here
190  BOOST_CHECK_CLOSE(val, requiredVal, 0.01);
191  BOOST_CHECK_CLOSE(float(accessor), requiredVal, 0.01);
192  BOOST_CHECK_CLOSE(2. * accessor, 2 * requiredVal, 0.01);
193  BOOST_CHECK_CLOSE(accessor + 2, 2 + requiredVal, 0.01);
194  dummy = -10;
195  BOOST_CHECK_CLOSE(float(accessor), requiredVal, 0.01);
196  accessor.read();
197  BOOST_CHECK_CLOSE(float(accessor), 0, 0.01);
198 
199  // test assignment etc. for writing
200  accessor = -4;
201  accessor.write();
202  BOOST_CHECK_CLOSE(float(dummy), 0, 0.01);
203  accessor = 10.3125;
204  accessor.write();
205  BOOST_CHECK_CLOSE(float(dummy), 10.3125, 0.01);
206 
207  device.close();
208 }
209 
210 /**********************************************************************************************************************/
211 
213 BOOST_AUTO_TEST_CASE(testWordOffset) {
214  setDMapFilePath("dummies.dmap");
215  std::cout << "testWordOffset" << std::endl;
216 
217  Device device;
218  device.open("DUMMYD2");
219  boost::shared_ptr<DummyBackend> backend =
220  boost::dynamic_pointer_cast<DummyBackend>(BackendFactory::getInstance().createBackend("DUMMYD2"));
221  BOOST_CHECK(backend != NULL);
222 
223  // The second entry in module 1 is WORD_USER2
224  DummyRegisterAccessor<float> dummy(backend.get(), "MODULE1", "WORD_USER2");
225  dummy = 3.5;
226 
227  // obtain register accessor with integral type. We use and offset of 1 (second
228  // word in module1), and raw mode to check that argument passing works
229  ScalarRegisterAccessor<int> accessor = device.getScalarRegisterAccessor<int>("APP0/MODULE1", 1, {AccessMode::raw});
230  accessor.read();
231  BOOST_CHECK(accessor == static_cast<int>(3.5 * (1 << 5))); // 5 fractional bits, float value 3.5
232 
233  // Just to be safe that we don't accidentally have another register with
234  // content 112: modify it
235  ++accessor;
236  accessor.write();
237  BOOST_CHECK(dummy == 3.53125);
238 
239  device.close();
240 }
241 
242 /**********************************************************************************************************************/
243 
244 BOOST_AUTO_TEST_CASE(testUniqueID) {
245  setDMapFilePath("dummies.dmap");
246  std::cout << "testUniqueID" << std::endl;
247 
248  Device device;
249  device.open("DUMMYD2");
250 
251  // get register accessors
252  ScalarRegisterAccessor<int> accessor1 = device.getScalarRegisterAccessor<int>("APP0/MODULE0", 1, {AccessMode::raw});
253  ScalarRegisterAccessor<int> accessor2 = device.getScalarRegisterAccessor<int>("APP0/MODULE1", 1, {AccessMode::raw});
254 
255  // self consistency check
256  BOOST_CHECK(accessor1.getId() == accessor1.getId());
257  BOOST_CHECK(!(accessor1.getId() != accessor1.getId()));
258  BOOST_CHECK(accessor2.getId() == accessor2.getId());
259  BOOST_CHECK(!(accessor2.getId() != accessor2.getId()));
260  BOOST_CHECK(accessor1.getId() != accessor2.getId());
261  BOOST_CHECK(!(accessor1.getId() == accessor2.getId()));
262  BOOST_CHECK(accessor2.getId() != accessor1.getId());
263  BOOST_CHECK(!(accessor2.getId() == accessor1.getId()));
264 
265  // copy the abstractor and check if unique ID stays the same
266  ScalarRegisterAccessor<int> accessor1Copied;
267  accessor1Copied.replace(accessor1);
268  BOOST_CHECK(accessor1Copied.getId() == accessor1.getId());
269  BOOST_CHECK(accessor1Copied.getId() != accessor2.getId());
270  ScalarRegisterAccessor<int> accessor2Copied;
271  accessor2Copied.replace(accessor2);
272  BOOST_CHECK(accessor2Copied.getId() == accessor2.getId());
273  BOOST_CHECK(accessor2Copied.getId() != accessor1.getId());
274 
275  // compare with accessor for same register but created another time
276  ScalarRegisterAccessor<int> accessor1a = device.getScalarRegisterAccessor<int>("APP0/MODULE0", 1, {AccessMode::raw});
277  BOOST_CHECK(accessor1a.getId() == accessor1a.getId());
278  BOOST_CHECK(accessor1.getId() != accessor1a.getId());
279  BOOST_CHECK(accessor2.getId() != accessor1a.getId());
280 
281  // test storing the ID
282  TransferElementID myId;
283  BOOST_CHECK(myId != myId);
284  myId = accessor1.getId();
285  BOOST_CHECK(myId == accessor1.getId());
286  BOOST_CHECK(myId == accessor1Copied.getId());
287  BOOST_CHECK(myId != accessor2.getId());
288  BOOST_CHECK(myId != accessor1a.getId());
289 
290  // check if we can put the ID into an std::unordered_map as a key
291  std::unordered_map<TransferElementID, std::string> map1;
292  map1.insert({myId, "SomeTest"});
293  BOOST_CHECK(map1[accessor1.getId()] == "SomeTest");
294 
295  // check if we can put the ID into an std::unordered_map as a value
296  std::unordered_map<std::string, TransferElementID> map2;
297  map2.insert({"AnotherTest", myId});
298  BOOST_CHECK(map2["AnotherTest"] == accessor1.getId());
299 
300  // check if we can put the ID into an std::map as a key
301  std::map<TransferElementID, std::string> map3;
302  map3.insert({myId, "SomeTest"});
303  BOOST_CHECK(map3[accessor1.getId()] == "SomeTest");
304 
305  // check if we can put the ID into an std::map as a value
306  std::unordered_map<std::string, TransferElementID> map4;
307  map4.insert({"AnotherTest", myId});
308  BOOST_CHECK(map4["AnotherTest"] == accessor1.getId());
309 
310  // check if we can put the ID into an std::vector
311  std::vector<TransferElementID> vector;
312  vector.push_back(myId);
313  BOOST_CHECK(vector[0] == accessor1.getId());
314 
315  device.close();
316 }
317 
318 /**********************************************************************************************************************/
319 
320 BOOST_AUTO_TEST_CASE(testWriteIfDifferent) {
321  std::cout << "testWriteIfDifferent" << std::endl;
322  setDMapFilePath("dummies.dmap");
323 
324  Device device;
325  device.open("(WriteCountingDummy?map=goodMapFile.map)");
326  auto backend = boost::dynamic_pointer_cast<WriteCountingBackend>(
327  BackendFactory::getInstance().createBackend("(WriteCountingDummy?map=goodMapFile.map)"));
328  BOOST_CHECK(backend != NULL);
329 
330  // obtain register accessor with integral type
331  ScalarRegisterAccessor<int> accessor = device.getScalarRegisterAccessor<int>("APP0/WORD_STATUS");
332  BOOST_CHECK(accessor.isReadOnly() == false);
333  BOOST_CHECK(accessor.isReadable());
334  BOOST_CHECK(accessor.isWriteable());
335 
336  // dummy register accessor for comparison
337  DummyRegisterAccessor<int> dummy(backend.get(), "APP0", "WORD_STATUS");
338 
339  // Inital write and writeIfDifferent with same value
340  accessor = 501;
341  accessor.write();
342  size_t counterBefore = backend->writeCount;
343  accessor.writeIfDifferent(501); // should not write
344  size_t counterAfter = backend->writeCount;
345  BOOST_CHECK(counterBefore == counterAfter);
346 
347  // writeIfDifferent with different value
348  counterBefore = backend->writeCount;
349  accessor.writeIfDifferent(502); // should write
350  counterAfter = backend->writeCount;
351  BOOST_CHECK(counterAfter == counterBefore + 1);
352 
353  // writeIfDifferent with same value, but explicit version number
354  counterBefore = backend->writeCount;
355  accessor.writeIfDifferent(502, VersionNumber{}); // should not write
356  counterAfter = backend->writeCount;
357  BOOST_CHECK(counterAfter == counterBefore);
358 
359  // writeIfDifferent with different value, and explicit version number
360  counterBefore = backend->writeCount;
361  accessor.writeIfDifferent(514, VersionNumber{}); // should write
362  counterAfter = backend->writeCount;
363  BOOST_CHECK(counterAfter == counterBefore + 1);
364 
365  // writeIfDifferent with same value, but different DataValidity
366  counterBefore = backend->writeCount;
367  accessor.writeIfDifferent(514, VersionNumber{nullptr}, DataValidity::faulty); // should write
368  counterAfter = backend->writeCount;
369  BOOST_CHECK(counterAfter == counterBefore + 1);
370 
371  // writeIfDifferent with same value, but different DataValidity (now back at OK)
372  counterBefore = backend->writeCount;
373  accessor.writeIfDifferent(514); // should write
374  counterAfter = backend->writeCount;
375  BOOST_CHECK(counterAfter == counterBefore + 1);
376 
377  // test writeIfDifferent for newly created accessor:
378  ScalarRegisterAccessor<int> freshAccessor = device.getScalarRegisterAccessor<int>("APP0/WORD_STATUS");
379  counterBefore = backend->writeCount;
380  VersionNumber vn = freshAccessor.getVersionNumber();
381  BOOST_CHECK(vn == VersionNumber{nullptr});
382  freshAccessor.writeIfDifferent(0); // should write
383  vn = freshAccessor.getVersionNumber();
384  BOOST_CHECK(vn != VersionNumber{nullptr});
385  counterAfter = backend->writeCount;
386  BOOST_CHECK(counterBefore != counterAfter);
387 
388  device.close();
389 }
390 
391 /**********************************************************************************************************************/
ChimeraTK::TransferElementAbstractor::getId
TransferElementID getId() const
Obtain unique ID for the actual implementation of this TransferElement.
Definition: TransferElementAbstractor.h:192
ChimeraTK::TransferElementAbstractor::isReadOnly
bool isReadOnly() const
Check if transfer element is read only, i.e.
Definition: TransferElementAbstractor.h:102
ChimeraTK::TransferElementAbstractor::isWriteable
bool isWriteable() const
Check if transfer element is writeable.
Definition: TransferElementAbstractor.h:112
device
ctk::Device device
Definition: testExceptionDummyDevice.cc:18
ChimeraTK::DummyRegisterAccessor< int >
ChimeraTK::Device::close
void close()
Close the device.
Definition: Device.cc:66
DummyBackend.h
ChimeraTK::ScalarRegisterAccessor::setAndWrite
void setAndWrite(UserType newValue, VersionNumber versionNumber={})
Convenience function to set and write new value.
Definition: ScalarRegisterAccessor.h:292
ChimeraTK::TransferElementAbstractor::isInitialised
bool isInitialised() const
Return if the accessor is properly initialised.
Definition: TransferElementAbstractor.h:152
ChimeraTK::ScalarRegisterAccessor::writeIfDifferent
void writeIfDifferent(UserType newValue, VersionNumber versionNumber=VersionNumber{nullptr}, DataValidity validity=DataValidity::ok)
Convenience function to set and write new value if it differes from the current value.
Definition: ScalarRegisterAccessor.h:276
ChimeraTK::ScalarRegisterAccessor::readAndGet
UserType readAndGet()
Convenience function to read and return a value of UserType.
Definition: ScalarRegisterAccessor.h:300
ChimeraTK::ScalarRegisterAccessor
Accessor class to read and write scalar registers transparently by using the accessor object like a v...
Definition: ScalarRegisterAccessor.h:24
accessPrivateData.h
ChimeraTK::TransferElementAbstractor::isReadable
bool isReadable() const
Check if transfer element is readable.
Definition: TransferElementAbstractor.h:107
ScalarRegisterAccessor.h
ChimeraTK::NDRegisterAccessorAbstractor::replace
void replace(const NDRegisterAccessorAbstractor< UserType > &newAccessor)
Assign a new accessor to this NDRegisterAccessorAbstractor.
Definition: NDRegisterAccessorAbstractor.h:67
Device.h
ChimeraTK::Device
Class allows to read/write registers from device.
Definition: Device.h:39
ChimeraTK::Device::open
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition: Device.cc:58
DummyRegisterAccessor.h
ChimeraTK::Device::getScalarRegisterAccessor
ScalarRegisterAccessor< UserType > getScalarRegisterAccessor(const RegisterPath &registerPathName, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a ScalarRegisterObject object for the given register.
Definition: Device.h:263
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(testCreation)
Definition: testScalarRegisterAccessor.cpp:30
ChimeraTK::VersionNumber
Class for generating and holding version numbers without exposing a numeric representation.
Definition: VersionNumber.h:23
WriteCountingBackend.h
ChimeraTK::TransferElementAbstractor::write
bool write(ChimeraTK::VersionNumber versionNumber={})
Write the data to device.
Definition: TransferElementAbstractor.h:89
ChimeraTK::TransferElementID
Simple class holding a unique ID for a TransferElement.
Definition: TransferElementID.h:17
ChimeraTK::setDMapFilePath
void setDMapFilePath(std::string dmapFilePath)
Set the location of the dmap file.
Definition: Utilities.cpp:327
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::TransferElementAbstractor::getVersionNumber
ChimeraTK::VersionNumber getVersionNumber() const
Returns the version number that is associated with the last transfer (i.e.
Definition: TransferElementAbstractor.h:83
ChimeraTK::TransferElementAbstractor::read
void read()
Read the data from the device.
Definition: TransferElementAbstractor.h:57