ChimeraTK-DeviceAccess  03.18.00
testUioBackendUnified.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 #include <filesystem>
5 #define BOOST_TEST_DYN_LINK
6 #define BOOST_TEST_MODULE UioBackendUnifiedTest
7 #include <boost/test/unit_test.hpp>
8 using namespace boost::unit_test_framework;
9 
10 #include "MapFileParser.h"
11 #include "UnifiedBackendTest.h"
12 #include <sys/file.h>
13 #include <sys/mman.h>
14 
15 #include <fcntl.h>
16 #include <fstream>
17 #include <iostream>
18 #include <poll.h>
19 #include <unistd.h>
20 
21 using namespace ChimeraTK;
22 
23 /**********************************************************************************************************************/
24 
25 // Use a file lock to ensure we are not running concurrent tests in
26 // parallel using the same kernel dummy driver.
27 //
28 // Note: The lock is automatically released when the process terminates!
29 struct TestLocker {
30  const std::string lockfile{"/var/run/lock/uiodummy.lock"};
32  int fd = open(lockfile.c_str(), O_WRONLY | O_CREAT, 0777);
33  if(fd == -1) {
34  exit(1);
35  }
36 
37  // obtain lock
38  int res = flock(fd, LOCK_EX);
39  if(res == -1) {
40  exit(1);
41  }
42  }
43 
45  // FIXME: It would be nice to unlink the file here, so it does not unnecessarily remain in the file system.
46  // Unfortunately, this somehow spoils the locking. Completely unclear why.
47 
48  // unlink(lockfile.c_str());
49  }
50 };
51 static TestLocker testLocker;
52 
53 /**********************************************************************************************************************/
54 
55 BOOST_AUTO_TEST_SUITE(UioBackendUnifiedTestSuite)
56 
57 /**********************************************************************************************************************/
58 
59 static const std::string cdd("(uio:ctkuiodummy?map=uioBackendTest.mapp)");
60 
61 /**********************************************************************************************************************/
62 
63 class RawUioAccess {
64  public:
65  explicit RawUioAccess(const std::string& filePath, const std::string& mapFile);
66  ~RawUioAccess();
67  void sendInterrupt() const;
68  [[nodiscard]] size_t getMemorySize() const;
69  void* data();
70  template<typename T>
71  T read(const std::string& name);
72  template<typename T>
73  void write(const std::string& name, T value);
74 
75  private:
76  int _uioFileDescriptor;
77  int _uioProcFd;
78  std::filesystem::path _deviceFilePath;
79  size_t _deviceMemSize = 0;
80  void* _memoryPointer{nullptr};
82 
83  static uint64_t readUint64HexFromFile(const std::string& filePath);
84 };
85 
86 /**********************************************************************************************************************/
87 
88 RawUioAccess::RawUioAccess(const std::string& filePath, const std::string& mapFile)
89 : _deviceFilePath(filePath.c_str()) {
90  if(std::filesystem::is_symlink(_deviceFilePath)) {
91  _deviceFilePath = std::filesystem::canonical(_deviceFilePath);
92  }
93  _uioFileDescriptor = open(filePath.c_str(), O_RDWR);
94 
95  if(_uioFileDescriptor < 0) {
96  throw std::runtime_error("failed to open UIO device '" + filePath + "'");
97  }
98 
99  _uioProcFd = open("/proc/uio-dummy", O_RDWR);
100  if(_uioProcFd < 0) {
101  throw std::runtime_error("failed to open UIO device '" + filePath + "'");
102  }
103 
104  // Determine size of UIO memory region
105  std::string fileName = _deviceFilePath.filename().string();
106  _deviceMemSize = readUint64HexFromFile("/sys/class/uio/" + fileName + "/maps/map0/size");
107 
108  _memoryPointer = mmap(nullptr, _deviceMemSize, PROT_READ | PROT_WRITE, MAP_SHARED, _uioFileDescriptor, 0);
109 
110  if(_memoryPointer == MAP_FAILED) {
111  throw std::runtime_error("UioMmap construction failed");
112  }
113  MapFileParser p;
114  auto [cat, metaCat] = p.parse(mapFile);
115  _catalogue = std::move(cat);
116 }
117 
118 /**********************************************************************************************************************/
119 /* Helper implementations */
120 /**********************************************************************************************************************/
121 
123  munmap(_memoryPointer, _deviceMemSize);
124  close(_uioFileDescriptor);
125  close(_uioProcFd);
126 }
127 
128 /**********************************************************************************************************************/
129 
131  static int enable{1};
132  std::ignore = ::write(_uioProcFd, &enable, sizeof(enable));
133 }
134 
135 /**********************************************************************************************************************/
136 
138  return _deviceMemSize;
139 }
140 
141 /**********************************************************************************************************************/
142 
144  return _memoryPointer;
145 }
146 
147 /**********************************************************************************************************************/
148 
149 template<typename T>
150 T RawUioAccess::read(const std::string& name) {
151  auto r = _catalogue.getBackendRegister(name);
152  return *reinterpret_cast<T*>(reinterpret_cast<std::byte*>(data()) + r.address);
153 }
154 
155 /**********************************************************************************************************************/
156 
157 template<typename T>
158 void RawUioAccess::write(const std::string& name, T value) {
159  auto r = _catalogue.getBackendRegister(name);
160  *reinterpret_cast<T*>(reinterpret_cast<std::byte*>(data()) + r.address) = value;
161  sendInterrupt();
162 }
163 
164 /**********************************************************************************************************************/
165 
166 uint64_t RawUioAccess::readUint64HexFromFile(const std::string& filePath) {
167  uint64_t value = 0;
168  std::ifstream inputFile(filePath.c_str());
169 
170  if(inputFile.is_open()) {
171  inputFile >> std::hex >> value;
172  inputFile.close();
173  }
174  return value;
175 }
176 
177 /**********************************************************************************************************************/
178 /* The test descriptors */
179 /**********************************************************************************************************************/
180 
181 template<typename Derived>
183  Derived* derived = static_cast<Derived*>(this);
184  AccessModeFlags supportedFlags() { return {AccessMode::raw}; }
185  size_t nChannels() { return 1; }
186  size_t nElementsPerChannel() { return 1; }
187  size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
188  size_t nRuntimeErrorCases() { return 0; }
189  using minimumUserType = int32_t;
191 
192  static constexpr auto capabilities = TestCapabilities<>()
194  .disableSwitchReadOnly()
195  .disableSwitchWriteOnly()
196  .disableTestWriteNeverLosesData()
197  .disableAsyncReadInconsistency()
198  .enableTestRawTransfer();
199 
200  std::shared_ptr<RawUioAccess> remote{new RawUioAccess("/dev/ctkuiodummy", "uioBackendTest.mapp")};
201 
202  // Type can be raw type or user type
203  template<typename Type>
204  std::vector<std::vector<Type>> generateValue(bool raw = false) {
205  auto rawVal00 = remote->read<rawUserType>(derived->path());
206  rawVal00 += 3;
207  Type val00 = (raw ? rawVal00 : rawToCooked<Type, rawUserType>(rawVal00));
208 
209  return {{val00}};
210  }
211 
212  // Type can be raw type or user type
213  template<typename Type>
214  std::vector<std::vector<Type>> getRemoteValue(bool raw = false) {
215  auto rawVal00 = remote->read<rawUserType>(derived->path());
216  Type val00 = (raw ? rawVal00 : rawToCooked<Type, rawUserType>(rawVal00));
217 
218  return {{val00}};
219  }
220 
221  void setRemoteValue() {
222  auto x = generateValue<rawUserType>(true)[0][0];
223  remote->write<rawUserType>(derived->path(), x);
224  }
225 
226  // default implementation just casting. Re-implement in derived classes if needed.
227  template<typename UserType, typename RawType>
228  RawType cookedToRaw(UserType val) {
229  return static_cast<RawType>(val);
230  }
231 
232  // default implementation just casting. Re-implement in derived classes if needed.
233  template<typename UserType, typename RawType>
234  UserType rawToCooked(RawType val) {
235  return static_cast<UserType>(val);
236  }
237 
238  // we need this because it's expected in template, but unused
239  void setForceRuntimeError([[maybe_unused]] bool enable, [[maybe_unused]] size_t type) {}
240 };
241 
242 /**********************************************************************************************************************/
243 
244 struct Scalar32 : ScalarDescriptor<Scalar32> {
245  static std::string path() { return "TIMING.WORD_ID"; }
246  static bool isReadable() { return true; }
247  static bool isWriteable() { return false; }
248 };
249 
250 /**********************************************************************************************************************/
251 
252 struct Scalar32Async : ScalarDescriptor<Scalar32Async> {
253  static std::string path() { return "MOTOR_CONTROL.MOTOR_POSITION"; }
254  static bool isReadable() { return true; }
255  static bool isWriteable() { return false; }
258  }
259 };
260 
261 /**********************************************************************************************************************/
262 
263 BOOST_AUTO_TEST_CASE(testUnified) {
264  UnifiedBackendTest<>().addRegister<Scalar32>().addRegister<Scalar32Async>().runTests(cdd);
265 }
266 
267 /**********************************************************************************************************************/
268 
269 BOOST_AUTO_TEST_SUITE_END()
RawUioAccess::write
void write(const std::string &name, T value)
Definition: testUioBackendUnified.cpp:158
ScalarDescriptor::getRemoteValue
std::vector< std::vector< Type > > getRemoteValue(bool raw=false)
Definition: testUioBackendUnified.cpp:214
ScalarDescriptor::nRuntimeErrorCases
size_t nRuntimeErrorCases()
Definition: testUioBackendUnified.cpp:188
ChimeraTK::AccessMode::raw
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
ChimeraTK::NumericAddressedRegisterCatalogue::getBackendRegister
NumericAddressedRegisterInfo getBackendRegister(const RegisterPath &registerPathName) const override
Note: Override this function if backend has "hidden" registers which are not added to the map and hen...
Definition: NumericAddressedRegisterCatalogue.cc:209
MapFileParser.h
ScalarDescriptor::writeQueueLength
size_t writeQueueLength()
Definition: testUioBackendUnified.cpp:187
ScalarDescriptor::derived
Derived * derived
Definition: testUioBackendUnified.cpp:183
RawUioAccess::RawUioAccess
RawUioAccess(const std::string &filePath, const std::string &mapFile)
Definition: testUioBackendUnified.cpp:88
ScalarDescriptor::capabilities
static constexpr auto capabilities
Definition: testUioBackendUnified.cpp:192
TestLocker
Definition: testPcieBackend.cpp:51
ScalarDescriptor::cookedToRaw
RawType cookedToRaw(UserType val)
Definition: testUioBackendUnified.cpp:228
RawUioAccess::sendInterrupt
void sendInterrupt() const
Definition: testUioBackendUnified.cpp:130
Scalar32
Definition: testUioBackendUnified.cpp:244
ScalarDescriptor
Definition: testUioBackendUnified.cpp:182
ChimeraTK::MapFileParser
Provides method to parse MAP file.
Definition: MapFileParser.h:21
cdd
std::string cdd
Definition: testAsyncRead.cpp:25
ChimeraTK::TestCapabilities
Descriptor for the test capabilities for each register.
Definition: UnifiedBackendTest.h:54
ScalarDescriptor::supportedFlags
AccessModeFlags supportedFlags()
Definition: testUioBackendUnified.cpp:184
Scalar32::path
static std::string path()
Definition: testUioBackendUnified.cpp:245
Scalar32Async::isReadable
static bool isReadable()
Definition: testUioBackendUnified.cpp:254
ScalarDescriptor< Scalar32 >::minimumUserType
int32_t minimumUserType
Definition: testUioBackendUnified.cpp:189
ScalarDescriptor::setForceRuntimeError
void setForceRuntimeError([[maybe_unused]] bool enable, [[maybe_unused]] size_t type)
Definition: testUioBackendUnified.cpp:239
ScalarDescriptor::nElementsPerChannel
size_t nElementsPerChannel()
Definition: testUioBackendUnified.cpp:186
ChimeraTK::AccessMode::wait_for_new_data
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
RawUioAccess
Definition: testUioBackendUnified.cpp:63
Scalar32::isReadable
static bool isReadable()
Definition: testUioBackendUnified.cpp:246
ScalarDescriptor::rawToCooked
UserType rawToCooked(RawType val)
Definition: testUioBackendUnified.cpp:234
Scalar32Async::supportedFlags
static ChimeraTK::AccessModeFlags supportedFlags()
Definition: testUioBackendUnified.cpp:256
ChimeraTK::UnifiedBackendTest
Class to test any backend for correct behaviour.
Definition: UnifiedBackendTest.h:259
ScalarDescriptor::remote
std::shared_ptr< RawUioAccess > remote
Definition: testUioBackendUnified.cpp:200
Scalar32::isWriteable
static bool isWriteable()
Definition: testUioBackendUnified.cpp:247
ChimeraTK::TestCapabilities::disableForceDataLossWrite
constexpr TestCapabilities< _syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableForceDataLossWrite() const
Definition: UnifiedBackendTest.h:81
TestLocker::TestLocker
TestLocker()
Definition: testUioBackendUnified.cpp:31
ScalarDescriptor::setRemoteValue
void setRemoteValue()
Definition: testUioBackendUnified.cpp:221
ScalarDescriptor::generateValue
std::vector< std::vector< Type > > generateValue(bool raw=false)
Definition: testUioBackendUnified.cpp:204
TestLocker::~TestLocker
~TestLocker()
Definition: testUioBackendUnified.cpp:44
ChimeraTK::TransferType::write
@ write
RawUioAccess::read
T read(const std::string &name)
Definition: testUioBackendUnified.cpp:150
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(testUnified)
Definition: testUioBackendUnified.cpp:263
ChimeraTK::NumericAddressedRegisterCatalogue
Definition: NumericAddressedRegisterCatalogue.h:136
ScalarDescriptor::nChannels
size_t nChannels()
Definition: testUioBackendUnified.cpp:185
RawUioAccess::getMemorySize
size_t getMemorySize() const
Definition: testUioBackendUnified.cpp:137
RawUioAccess::~RawUioAccess
~RawUioAccess()
Definition: testUioBackendUnified.cpp:122
ChimeraTK::AccessModeFlags
Set of AccessMode flags with additional functionality for an easier handling.
Definition: AccessMode.h:48
Scalar32Async::isWriteable
static bool isWriteable()
Definition: testUioBackendUnified.cpp:255
RawUioAccess::data
void * data()
Definition: testUioBackendUnified.cpp:143
Scalar32Async
Definition: testUioBackendUnified.cpp:252
ChimeraTK
Definition: DummyBackend.h:16
UnifiedBackendTest.h
Scalar32Async::path
static std::string path()
Definition: testUioBackendUnified.cpp:253
ChimeraTK::TransferType::read
@ read
ScalarDescriptor< Scalar32 >::rawUserType
minimumUserType rawUserType
Definition: testUioBackendUnified.cpp:190
ChimeraTK::UnifiedBackendTest::addRegister
UnifiedBackendTest< typename boost::mpl::push_back< VECTOR_OF_REGISTERS_T, REG_T >::type > addRegister()
Add a register to be used by the test.
Definition: UnifiedBackendTest.h:352
ChimeraTK::MapFileParser::parse
std::pair< NumericAddressedRegisterCatalogue, MetadataCatalogue > parse(const std::string &file_name)
Performs parsing of specified MAP file.
Definition: MapFileParser.cpp:19