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