ChimeraTK-DeviceAccess 03.31.00
Loading...
Searching...
No Matches
NumericAddressedBackend.cc
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
5
6#include "async/DomainImpl.h"
8#include "BackendFactory.h"
11#include "Exception.h"
12#include "MapFileParser.h"
13#include "NumericAddress.h"
17#include "parserUtilities.h"
18#include "SupportedUserTypes.h"
19
20#include <nlohmann/json.hpp>
21
22#include <filesystem>
23
24using json = nlohmann::json;
25
26namespace ChimeraTK {
27
28 /********************************************************************************************************************/
29
31 std::unique_ptr<NumericAddressedRegisterCatalogue> registerMapPointer,
32 const std::string& dataConsistencyKeyDescriptor)
33 : _registerMapPointer(std::move(registerMapPointer)), _registerMap(*_registerMapPointer) {
34 FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(getRegisterAccessor_impl);
35 if(!mapFileName.empty()) {
36 _resolvedMapFileName = resolveMapFileName(mapFileName);
37 MapFileParser parser;
39 }
40 if(!dataConsistencyKeyDescriptor.empty()) {
41 // parse as JSON
42 try {
43 auto jdescr = nlohmann::json::parse(dataConsistencyKeyDescriptor);
44 for(const auto& el : jdescr.items()) {
45 _registerMap.addDataConsistencyRealm(el.key(), el.value());
46 }
47 }
48 catch(json::parse_error& e) {
49 throw ChimeraTK::logic_error(std::format("Parsing DataConsistencyKeys parameter '{}' results in JSON error: {}",
50 dataConsistencyKeyDescriptor, e.what()));
51 }
52 }
53 }
54
55 /********************************************************************************************************************/
56
57 std::string NumericAddressedBackend::resolveMapFileName(const std::string& mapFileName) {
58 // an absolute path is used directly, legacy DMAP entries are already delivered as absolute paths
59 if(mapFileName[0] == '/') {
60 if(std::filesystem::exists(mapFileName)) {
61 return std::filesystem::canonical(mapFileName).string();
62 }
63 throw ChimeraTK::logic_error("Cannot open map file \"" + mapFileName + "\": file not found.");
64 }
65
66 // relative path candidate: relative to the directory of the DMAP file (if a DMAP path is set)
67 std::string dmapFilePath = BackendFactory::getInstance().getDMapFilePath();
68 std::string dmapDir = parserUtilities::extractDirectory(dmapFilePath);
69 std::string candidateA = parserUtilities::concatenatePaths(dmapDir, mapFileName);
70 // a relative DMAP path is itself relative to the cwd; an unset DMAP path yields "./" as directory,
71 // so candidateA collapses to the cwd-relative path, which is the desired fallback anyway
72 if(std::filesystem::exists(candidateA)) {
73 return std::filesystem::canonical(candidateA).string();
74 }
75
76 // fallback candidate: relative to the current working directory
77 std::string candidateB = parserUtilities::convertToAbsolutePath(mapFileName);
78 if(std::filesystem::exists(candidateB)) {
79 return std::filesystem::canonical(candidateB).string();
80 }
81
82 throw ChimeraTK::logic_error("Cannot open map file \"" + mapFileName + "\": file not found.");
83 }
84
85 /********************************************************************************************************************/
86
90
91 /********************************************************************************************************************/
92
93 /* Throw exception if called directly and not implemented by backend */
94 void NumericAddressedBackend::read([[maybe_unused]] uint8_t bar, [[maybe_unused]] uint32_t address,
95 [[maybe_unused]] int32_t* data, [[maybe_unused]] size_t sizeInBytes) {
96 throw ChimeraTK::logic_error("NumericAddressedBackend: internal error: interface read() called w/ 32bit address");
97 }
98
99 /********************************************************************************************************************/
100
101 void NumericAddressedBackend::write([[maybe_unused]] uint8_t bar, [[maybe_unused]] uint32_t address,
102 [[maybe_unused]] int32_t const* data, [[maybe_unused]] size_t sizeInBytes) {
103 throw ChimeraTK::logic_error("NumericAddressedBackend: internal error: interface write() called w/ 32bit address");
104 }
105
106 /********************************************************************************************************************/
107
108 /* Call 32-bit address implementation by default, for backends that don't implement 64-bit */
109 void NumericAddressedBackend::read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) {
110#pragma GCC diagnostic push
111#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
112 read(static_cast<uint8_t>(bar), static_cast<uint32_t>(address), data, sizeInBytes);
113#pragma GCC diagnostic pop
114 }
115
116 /********************************************************************************************************************/
117
118 void NumericAddressedBackend::write(uint64_t bar, uint64_t address, int32_t const* data, size_t sizeInBytes) {
119#pragma GCC diagnostic push
120#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
121 write(static_cast<uint8_t>(bar), static_cast<uint32_t>(address), data, sizeInBytes);
122#pragma GCC diagnostic pop
123 }
124
125 /********************************************************************************************************************/
126
127 // Default range of valid BARs
129 return bar <= 5 || bar == 13;
130 }
131
132 /********************************************************************************************************************/
133
134 template<typename UserType>
135 boost::shared_ptr<NDRegisterAccessor<UserType>> NumericAddressedBackend::getRegisterAccessor_impl(
136 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
138 // get the interrupt information from the map file
139 auto registerInfo = _registerMap.getBackendRegister(registerPathName);
140 if(!registerInfo.getSupportedAccessModes().has(AccessMode::wait_for_new_data)) {
142 "Register " + registerPathName + " does not support AccessMode::wait_for_new_data.");
143 }
144
145 return _asyncDomainsContainer.subscribe<NumericAddressedBackend, std::nullptr_t, UserType>(
146 boost::static_pointer_cast<NumericAddressedBackend>(shared_from_this()),
147 registerInfo.getQualifiedAsyncId().front(), _asyncIsActive, registerPathName, numberOfWords,
148 wordOffsetInRegister, flags);
149 }
150 return getSyncRegisterAccessor<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
151 }
152
153 /********************************************************************************************************************/
154
155 template<typename UserType>
156 boost::shared_ptr<NDRegisterAccessor<UserType>> NumericAddressedBackend::getSyncRegisterAccessor(
157 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
158 boost::shared_ptr<NDRegisterAccessor<UserType>> accessor;
159 // obtain register info
160 auto registerInfo = getRegisterInfo(registerPathName);
161 if(registerInfo.doubleBuffer == std::nullopt) {
162 // 1D or scalar register
163 if(registerInfo.getNumberOfDimensions() <= 1) {
164 if(registerInfo.isBitRange) {
165 if(numberOfWords == 0) {
166 numberOfWords = registerInfo.nElements == 0 ? 1 : registerInfo.nElements;
167 }
168 size_t nParentElements = registerInfo.nElements == 0 ? 1 : registerInfo.nElements;
169 if(numberOfWords + wordOffsetInRegister > nParentElements) {
170 throw ChimeraTK::logic_error("Error in '" + registerPathName + "': Requested number of words+offset " +
171 std::to_string(numberOfWords + wordOffsetInRegister) + " exceeds register size of " +
172 std::to_string(nParentElements) + ".");
173 }
174 // Target must point to the full parent register (all elements). The element data width of the slice's
175 // sample word is derived from the channel raw type via DataType::getNumberOfBytes(), so the 'u<width>'
176 // marker in the target path matches the sample word width (which may differ from the element pitch for
177 // strided slices) instead of being hardcoded to 64 as before.
178 uint64_t targetSizeBytes = nParentElements * registerInfo.elementPitchBits / 8;
179 uint32_t wordWidth = registerInfo.channels.front().getRawType().getNumberOfBytes() * 8;
180 RegisterPath targetRegisterPath = numeric_address::BAR() / std::to_string(registerInfo.bar) /
181 (std::to_string(registerInfo.address) + "*" + std::to_string(targetSizeBytes) + "u" +
182 std::to_string(wordWidth) + "p" + std::to_string(registerInfo.elementPitchBits));
183 auto target = getSyncRegisterAccessor<uint64_t>(targetRegisterPath, nParentElements, 0, {});
184
185 if(flags.has(AccessMode::raw)) {
186 return boost::make_shared<detail::BitRangeAccessorDecorator<UserType, true>>(
187 shared_from_this(), targetRegisterPath, target, registerInfo, numberOfWords, wordOffsetInRegister);
188 }
189 return boost::make_shared<detail::BitRangeAccessorDecorator<UserType, false>>(
190 shared_from_this(), targetRegisterPath, target, registerInfo, numberOfWords, wordOffsetInRegister);
191 }
192 if(registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::FIXED_POINT ||
193 registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::VOID ||
194 registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::IEEE754) {
195 if(flags.has(AccessMode::raw)) {
196 accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(
197 new NumericAddressedBackendRegisterAccessor<UserType, true>(
198 shared_from_this(), registerPathName, numberOfWords, wordOffsetInRegister, flags));
199 }
200 else {
201 accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(
202 new NumericAddressedBackendRegisterAccessor<UserType, false>(
203 shared_from_this(), registerPathName, numberOfWords, wordOffsetInRegister, flags));
204 }
205 }
206 else if(registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::ASCII) {
207 if constexpr(!std::is_same<UserType, std::string>::value) {
208 throw ChimeraTK::logic_error("NumericAddressedBackend: ASCII data must be read with std::string UserType.");
209 }
210 else {
211 accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(new NumericAddressedBackendASCIIAccessor(
212 shared_from_this(), registerPathName, numberOfWords, wordOffsetInRegister, flags));
213 }
214 }
215 else {
216 throw ChimeraTK::logic_error("NumericAddressedBackend: trying to get accessor for unsupported data type");
217 }
218 }
219 // 2D multiplexed register
220 else {
221 flags.checkForUnknownFlags({});
222 accessor =
223 boost::shared_ptr<NDRegisterAccessor<UserType>>(new NumericAddressedBackendMuxedRegisterAccessor<UserType>(
224 registerPathName, numberOfWords, wordOffsetInRegister, shared_from_this()));
225 }
226 }
227 // double buffer register
228 else {
229 if(numberOfWords == 0) {
230 numberOfWords = registerInfo.getNumberOfElements();
231 }
232 const auto& enableRegPath = registerInfo.doubleBuffer->enableRegisterPath;
233 auto& controlState = _doubleBufferMutexMap[enableRegPath];
234 if(!controlState) {
235 controlState = std::make_shared<detail::CountedRecursiveMutex>();
236 }
237 accessor = boost::make_shared<DoubleBufferAccessor<UserType>>(*registerInfo.doubleBuffer, shared_from_this(),
238 controlState, registerPathName, numberOfWords, wordOffsetInRegister, flags);
239 }
240 accessor->setExceptionBackend(shared_from_this());
241 return accessor;
242 }
243
244 /********************************************************************************************************************/
245
247 _asyncIsActive = true;
248 // Iterating all async domains must happen under the container lock. We prepare a lambda that is executed via
249 // DomainsContainer::forEach().
250 auto activateDomain = [this](size_t key, boost::shared_ptr<async::Domain>& domain) {
251 auto domainImpl = boost::dynamic_pointer_cast<async::DomainImpl<std::nullptr_t>>(domain);
252 assert(domainImpl);
253 auto subscriptionDone = this->activateSubscription(key, domainImpl);
254 // Wait until the backends reports that the subscription is complete (typically set from inside another thread)
255 // before polling the initial values when activating the async domain. This is necessary to make sure we don't
256 // miss an update that came in after polling the initial value.
257 subscriptionDone.wait();
258 domainImpl->activate(nullptr);
259 };
260
261 _asyncDomainsContainer.forEach(activateDomain);
262 }
263
264 /********************************************************************************************************************/
265
266 // The default implementation just returns a ready future.
267 std::future<void> NumericAddressedBackend::activateSubscription([[maybe_unused]] unsigned int interruptNumber,
268 [[maybe_unused]] boost::shared_ptr<async::DomainImpl<std::nullptr_t>> asyncDomain) {
269 std::promise<void> subscriptionDonePromise;
270 subscriptionDonePromise.set_value();
271 return subscriptionDonePromise.get_future();
272 }
273
274 /********************************************************************************************************************/
275
277 _asyncIsActive = false;
278
279 _asyncDomainsContainer.forEach([](size_t, boost::shared_ptr<async::Domain>& domain) { domain->deactivate(); });
280
281 closeImpl();
282 }
283
284 /********************************************************************************************************************/
285
289
290 /********************************************************************************************************************/
291
295
296 /********************************************************************************************************************/
297
299 _asyncIsActive = false;
300 }
301
302 /********************************************************************************************************************/
303
304} // namespace ChimeraTK
nlohmann::json json
#define FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(functionName)
Fill the vtable of a virtual function template defined with DEFINE_VIRTUAL_FUNCTION_TEMPLATE.
Set of AccessMode flags with additional functionality for an easier handling.
Definition AccessMode.h:48
bool has(AccessMode flag) const
Check if a certain flag is in the set.
Definition AccessMode.cc:20
static BackendFactory & getInstance()
Static function to get an instance of factory.
std::string getDMapFilePath()
Returns the _DMapFilePath.
async::DomainsContainer _asyncDomainsContainer
Container for async::Domains to support wait_for_new_data.
static std::pair< NumericAddressedRegisterCatalogue, MetadataCatalogue > parse(const std::string &fileName)
Performs parsing of specified MAP file, resulting in catalogue objects describing all registers and m...
Container for backend metadata.
MetadataCatalogue _metadataCatalogue
metadata catalogue
RegisterCatalogue getRegisterCatalogue() const override
Return the register catalogue with detailed information on all registers.
virtual bool barIndexValid(uint64_t bar)
Function to be implemented by the backends.
NumericAddressedRegisterCatalogue & _registerMap
NumericAddressedRegisterInfo getRegisterInfo(const RegisterPath &registerPathName)
getRegisterInfo returns a NumericAddressedRegisterInfo object for the given register.
NumericAddressedBackend(const std::string &mapFileName="", std::unique_ptr< NumericAddressedRegisterCatalogue > registerMapPointer=std::make_unique< NumericAddressedRegisterCatalogue >(), const std::string &dataConsistencyKeyDescriptor="")
void setExceptionImpl() noexcept override
Turn off the internal variable which remembers that async is active.
void close() final
Deactivates all asynchronous accessors and calls closeImpl().
MetadataCatalogue getMetadataCatalogue() const override
Return the device metadata catalogue.
void activateAsyncRead() noexcept override
Activate asyncronous read for all transfer elements where AccessMode::wait_for_new_data is set.
std::string _resolvedMapFileName
The resolved absolute path of the map file used for parsing and for any derived naming (e....
virtual void closeImpl()
All backends derrived from NumericAddressedBackend must implement closeImpl() instead of close.
virtual void write(uint64_t bar, uint64_t address, int32_t const *data, size_t sizeInBytes)
Write function to be implemented by backends.
virtual std::future< void > activateSubscription(uint32_t interruptNumber, boost::shared_ptr< async::DomainImpl< std::nullptr_t > > asyncDomain)
Activate/create the subscription for a given interrupt (for instance by starting the according interr...
virtual void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes)
Read function to be implemented by backends.
void addDataConsistencyRealm(const RegisterPath &registerPath, const std::string &realmName)
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...
std::unique_ptr< BackendRegisterCatalogueBase > clone() const override
Create deep copy of the catalogue.
Catalogue of register information.
Class to store a register path name.
boost::shared_ptr< AsyncNDRegisterAccessor< UserDataType > > subscribe(boost::shared_ptr< BackendType > backend, size_t domainId, bool activate, RegisterPath name, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Get an accessor from a particular domain.
void forEach(const std::function< void(size_t, boost::shared_ptr< Domain > &)> &executeMe)
Iterate all Domains under the container lock.
Exception thrown when a logic error has occured.
Definition Exception.h:51
RegisterPath BAR()
The numeric_address::BAR() function can be used to directly access registers by numeric addresses,...
std::string extractDirectory(std::string const &path)
Returns the path to the directory containing the file provided as the input parameter.
std::string concatenatePaths(const std::string &path1, const std::string &path2)
Concatenates two given paths using custom rules.
std::string convertToAbsolutePath(std::string const &relativePath)
Converts a relative path to its absolute path.
This header collects some conversion routines which should be useful both for ApplicationCore Python ...
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
STL namespace.
std::string to_string(const std::string &v)