ChimeraTK-DeviceAccess 03.25.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(testFloatRegisterAccessor) {
168 setDMapFilePath("dummies.dmap");
169 std::cout << "testFloatRegisterAccessor" << 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_CHECK(backend != NULL);
176
177 // obtain register accessor with integral type
178 ScalarRegisterAccessor<float> accessor = device.getScalarRegisterAccessor<float>("MODULE1/WORD_USER2");
179
180 // dummy register accessor for comparison
181 DummyRegisterAccessor<float> dummy(backend.get(), "MODULE1", "WORD_USER2");
182
183 // test type conversion etc. for reading
184 dummy = 5.3;
185 float requiredVal = dummy;
186 BOOST_CHECK_CLOSE(requiredVal, 5.3, 1);
187
188 accessor.read();
189 float val = accessor; // BOOST_CHECK_CLOSE requires implicit conversion in
190 // both directions, so we must help us here
191 BOOST_CHECK_CLOSE(val, requiredVal, 0.01);
192 BOOST_CHECK_CLOSE(float(accessor), requiredVal, 0.01);
193 BOOST_CHECK_CLOSE(2. * accessor, 2 * requiredVal, 0.01);
194 BOOST_CHECK_CLOSE(accessor + 2, 2 + requiredVal, 0.01);
195 dummy = -10;
196 BOOST_CHECK_CLOSE(float(accessor), requiredVal, 0.01);
197 accessor.read();
198 BOOST_CHECK_CLOSE(float(accessor), 0, 0.01);
199
200 // test assignment etc. for writing
201 accessor = -4;
202 accessor.write();
203 BOOST_CHECK_CLOSE(float(dummy), 0, 0.01);
204 accessor = 10.3125;
205 accessor.write();
206 BOOST_CHECK_CLOSE(float(dummy), 10.3125, 0.01);
207
208 device.close();
209}
210
211/**********************************************************************************************************************/
212
214BOOST_AUTO_TEST_CASE(testWordOffset) {
215 setDMapFilePath("dummies.dmap");
216 std::cout << "testWordOffset" << std::endl;
217
219 device.open("DUMMYD2");
220 boost::shared_ptr<DummyBackend> backend =
221 boost::dynamic_pointer_cast<DummyBackend>(BackendFactory::getInstance().createBackend("DUMMYD2"));
222 BOOST_CHECK(backend != NULL);
223
224 // The second entry in module 1 is WORD_USER2
225 DummyRegisterAccessor<float> dummy(backend.get(), "MODULE1", "WORD_USER2");
226 dummy = 3.5;
227
228 // obtain register accessor with integral type. We use and offset of 1 (second
229 // word in module1), and raw mode to check that argument passing works
230 ScalarRegisterAccessor<int> accessor = device.getScalarRegisterAccessor<int>("APP0/MODULE1", 1, {AccessMode::raw});
231 accessor.read();
232 BOOST_CHECK(accessor == static_cast<int>(3.5 * (1 << 5))); // 5 fractional bits, float value 3.5
233
234 // Just to be safe that we don't accidentally have another register with
235 // content 112: modify it
236 ++accessor;
237 accessor.write();
238 BOOST_CHECK(dummy == 3.53125);
239
240 device.close();
241}
242
243/**********************************************************************************************************************/
244
245BOOST_AUTO_TEST_CASE(testUniqueID) {
246 setDMapFilePath("dummies.dmap");
247 std::cout << "testUniqueID" << std::endl;
248
250 device.open("DUMMYD2");
251
252 // get register accessors
253 ScalarRegisterAccessor<int> accessor1 = device.getScalarRegisterAccessor<int>("APP0/MODULE0", 1, {AccessMode::raw});
254 ScalarRegisterAccessor<int> accessor2 = device.getScalarRegisterAccessor<int>("APP0/MODULE1", 1, {AccessMode::raw});
255
256 // self consistency check
257 BOOST_CHECK(accessor1.getId() == accessor1.getId());
258 BOOST_CHECK(!(accessor1.getId() != accessor1.getId()));
259 BOOST_CHECK(accessor2.getId() == accessor2.getId());
260 BOOST_CHECK(!(accessor2.getId() != accessor2.getId()));
261 BOOST_CHECK(accessor1.getId() != accessor2.getId());
262 BOOST_CHECK(!(accessor1.getId() == accessor2.getId()));
263 BOOST_CHECK(accessor2.getId() != accessor1.getId());
264 BOOST_CHECK(!(accessor2.getId() == accessor1.getId()));
265
266 // copy the abstractor and check if unique ID stays the same
267 ScalarRegisterAccessor<int> accessor1Copied;
268 accessor1Copied.replace(accessor1);
269 BOOST_CHECK(accessor1Copied.getId() == accessor1.getId());
270 BOOST_CHECK(accessor1Copied.getId() != accessor2.getId());
271 ScalarRegisterAccessor<int> accessor2Copied;
272 accessor2Copied.replace(accessor2);
273 BOOST_CHECK(accessor2Copied.getId() == accessor2.getId());
274 BOOST_CHECK(accessor2Copied.getId() != accessor1.getId());
275
276 // compare with accessor for same register but created another time
277 ScalarRegisterAccessor<int> accessor1a = device.getScalarRegisterAccessor<int>("APP0/MODULE0", 1, {AccessMode::raw});
278 BOOST_CHECK(accessor1a.getId() == accessor1a.getId());
279 BOOST_CHECK(accessor1.getId() != accessor1a.getId());
280 BOOST_CHECK(accessor2.getId() != accessor1a.getId());
281
282 // test storing the ID
284 BOOST_CHECK(myId != myId);
285 myId = accessor1.getId();
286 BOOST_CHECK(myId == accessor1.getId());
287 BOOST_CHECK(myId == accessor1Copied.getId());
288 BOOST_CHECK(myId != accessor2.getId());
289 BOOST_CHECK(myId != accessor1a.getId());
290
291 // check if we can put the ID into an std::unordered_map as a key
292 std::unordered_map<TransferElementID, std::string> map1;
293 map1.insert({myId, "SomeTest"});
294 BOOST_CHECK(map1[accessor1.getId()] == "SomeTest");
295
296 // check if we can put the ID into an std::unordered_map as a value
297 std::unordered_map<std::string, TransferElementID> map2;
298 map2.insert({"AnotherTest", myId});
299 BOOST_CHECK(map2["AnotherTest"] == accessor1.getId());
300
301 // check if we can put the ID into an std::map as a key
302 std::map<TransferElementID, std::string> map3;
303 map3.insert({myId, "SomeTest"});
304 BOOST_CHECK(map3[accessor1.getId()] == "SomeTest");
305
306 // check if we can put the ID into an std::map as a value
307 std::unordered_map<std::string, TransferElementID> map4;
308 map4.insert({"AnotherTest", myId});
309 BOOST_CHECK(map4["AnotherTest"] == accessor1.getId());
310
311 // check if we can put the ID into an std::vector
312 std::vector<TransferElementID> vector;
313 vector.push_back(myId);
314 BOOST_CHECK(vector[0] == accessor1.getId());
315
316 device.close();
317}
318
319/**********************************************************************************************************************/
320
321BOOST_AUTO_TEST_CASE(testWriteIfDifferent) {
322 std::cout << "testWriteIfDifferent" << std::endl;
323 setDMapFilePath("dummies.dmap");
324
326 device.open("(WriteCountingDummy?map=goodMapFile.map)");
327 auto backend = boost::dynamic_pointer_cast<WriteCountingBackend>(
328 BackendFactory::getInstance().createBackend("(WriteCountingDummy?map=goodMapFile.map)"));
329 BOOST_CHECK(backend != NULL);
330
331 // obtain register accessor with integral type
332 ScalarRegisterAccessor<int> accessor = device.getScalarRegisterAccessor<int>("APP0/WORD_STATUS");
333 BOOST_CHECK(accessor.isReadOnly() == false);
334 BOOST_CHECK(accessor.isReadable());
335 BOOST_CHECK(accessor.isWriteable());
336
337 // dummy register accessor for comparison
338 DummyRegisterAccessor<int> dummy(backend.get(), "APP0", "WORD_STATUS");
339
340 // Inital write and writeIfDifferent with same value
341 accessor = 501;
342 accessor.write();
343 size_t counterBefore = backend->writeCount;
344 accessor.writeIfDifferent(501); // should not write
345 size_t counterAfter = backend->writeCount;
346 BOOST_CHECK(counterBefore == counterAfter);
347
348 // writeIfDifferent with different value
349 counterBefore = backend->writeCount;
350 accessor.writeIfDifferent(502); // should write
351 counterAfter = backend->writeCount;
352 BOOST_CHECK(counterAfter == counterBefore + 1);
353
354 // writeIfDifferent with same value, but explicit version number
355 counterBefore = backend->writeCount;
356 accessor.writeIfDifferent(502, VersionNumber{}); // should not write
357 counterAfter = backend->writeCount;
358 BOOST_CHECK(counterAfter == counterBefore);
359
360 // writeIfDifferent with different value, and explicit version number
361 counterBefore = backend->writeCount;
362 accessor.writeIfDifferent(514, VersionNumber{}); // should write
363 counterAfter = backend->writeCount;
364 BOOST_CHECK(counterAfter == counterBefore + 1);
365
366 // writeIfDifferent with same value, but different DataValidity
367 counterBefore = backend->writeCount;
368 accessor.writeIfDifferent(514, VersionNumber{nullptr}, DataValidity::faulty); // should write
369 counterAfter = backend->writeCount;
370 BOOST_CHECK(counterAfter == counterBefore + 1);
371
372 // writeIfDifferent with same value, but different DataValidity (now back at OK)
373 counterBefore = backend->writeCount;
374 accessor.writeIfDifferent(514); // should write
375 counterAfter = backend->writeCount;
376 BOOST_CHECK(counterAfter == counterBefore + 1);
377
378 // test writeIfDifferent for newly created accessor:
379 ScalarRegisterAccessor<int> freshAccessor = device.getScalarRegisterAccessor<int>("APP0/WORD_STATUS");
380 counterBefore = backend->writeCount;
381 VersionNumber vn = freshAccessor.getVersionNumber();
382 BOOST_CHECK(vn == VersionNumber{nullptr});
383 freshAccessor.writeIfDifferent(0); // should write
384 vn = freshAccessor.getVersionNumber();
385 BOOST_CHECK(vn != VersionNumber{nullptr});
386 counterAfter = backend->writeCount;
387 BOOST_CHECK(counterBefore != counterAfter);
388
389 device.close();
390}
391
392/**********************************************************************************************************************/
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)