14 #include <boost/algorithm/string.hpp>
15 #include <boost/algorithm/string/predicate.hpp>
19 using namespace std::string_literals;
23 boost::shared_ptr<DeviceBackend> SubdeviceBackend::createInstance(
24 std::string address, std::map<std::string, std::string> parameters) {
25 if(parameters[
"map"].empty()) {
29 if(!address.empty()) {
30 if(parameters.size() > 1) {
33 "other than the map file in the device descriptor.");
36 std::vector<std::string> tokens;
37 boost::split(tokens, address, boost::is_any_of(
","));
38 if(tokens.size() != 3) {
40 "parameters in the address string.");
42 parameters[
"type"] = tokens[0];
43 parameters[
"device"] = tokens[1];
44 parameters[
"area"] = tokens[2];
52 SubdeviceBackend::SubdeviceBackend(std::map<std::string, std::string> parameters) {
56 if(parameters[
"type"].empty()) {
61 if(parameters[
"device"].empty()) {
63 "specified in the device descriptor.");
65 targetAlias = parameters[
"device"];
68 if(parameters[
"type"] ==
"area") {
71 else if(parameters[
"type"] ==
"areaHandshake") {
72 type = Type::areaHandshake;
75 else if(parameters[
"type"] ==
"3regs") {
76 type = Type::threeRegisters;
78 else if(parameters[
"type"] ==
"2regs") {
79 type = Type::twoRegisters;
81 if(parameters[
"sleep"].empty()) {
83 "descriptor for type '2regs'.");
93 if(parameters[
"area"].empty()) {
95 "must be specified in the device "
96 "descriptor for types 'area' and 'areaHandshake'.");
98 targetArea = parameters[
"area"];
102 if(parameters[
"data"].empty()) {
104 "name must be specified in the device "
105 "descriptor for types '2regs' and '3regs'.");
107 targetData = parameters[
"data"];
110 if(parameters[
"address"].empty()) {
112 "name must be specified in the device "
113 "descriptor for type '2regs' and '3regs'.");
115 targetAddress = parameters[
"address"];
117 if(!parameters[
"dataDelay"].empty()) {
119 addressToDataDelay = std::stoul(parameters[
"dataDelay"]);
121 catch(std::exception& e) {
123 parameters[
"dataDelay"] +
"': " + e.what());
127 if(needStatusParam()) {
128 if(parameters[
"status"].empty()) {
130 "name must be specified in the device "
131 "descriptor for types '3regs' and 'areaHandshake'.");
133 targetControl = parameters[
"status"];
134 if(!parameters[
"timeout"].empty()) {
136 timeout = std::stoul(parameters[
"timeout"]);
138 catch(std::exception& e) {
140 "SubdeviceBackend: Invalid value for parameter 'timeout': '" + parameters[
"timeout"] +
"': " + e.what());
145 if(!parameters[
"sleep"].empty()) {
147 sleepTime = std::stoul(parameters[
"sleep"]);
149 catch(std::exception& e) {
151 "SubdeviceBackend: Invalid value for parameter 'sleep': '" + parameters[
"sleep"] +
"': " + e.what());
155 if(parameters[
"map"].empty()) {
159 std::tie(_registerMap, _metadataCatalogue) = parser.
parse(parameters[
"map"]);
160 if(type == Type::twoRegisters || type == Type::threeRegisters) {
162 for(
auto info : _registerMap) {
164 info.registerAccess = NumericAddressedRegisterInfo::Access::WRITE_ONLY;
165 _registerMap.modifyRegister(info);
172 void SubdeviceBackend::obtainTargetBackend() {
173 if(targetDevice !=
nullptr)
return;
180 void SubdeviceBackend::open() {
181 obtainTargetBackend();
183 targetDevice->open();
184 setOpenedAndClearException();
189 void SubdeviceBackend::close() {
190 obtainTargetBackend();
191 targetDevice->close();
204 return _metadataCatalogue;
209 template<
typename UserType,
typename TargetUserType>
215 _fixedPointConverter(std::move(fixedPointConverter)) {}
220 _target->postRead(type, hasNewData);
221 if(!hasNewData)
return;
222 for(
size_t i = 0; i < this->buffer_2D.size(); ++i) {
223 _fixedPointConverter.template vectorToCooked<UserType>(
224 _target->accessChannel(i).begin(), _target->accessChannel(i).end(), buffer_2D[i].begin());
226 this->_dataValidity = _target->dataValidity();
227 this->_versionNumber = _target->getVersionNumber();
231 for(
size_t i = 0; i < this->buffer_2D.size(); ++i) {
232 for(
size_t j = 0; j < this->buffer_2D[i].size(); ++j) {
233 _target->accessChannel(i)[j] = _fixedPointConverter.toRaw<UserType>(buffer_2D[i][j]);
236 _target->setDataValidity(this->_dataValidity);
237 _target->preWrite(type, versionNumber);
241 _target->postWrite(type, versionNumber);
245 const boost::shared_ptr<ChimeraTK::TransferElement const>& other)
const override {
246 auto casted = boost::dynamic_pointer_cast<FixedPointConvertingDecorator<UserType, TargetUserType>
const>(other);
247 if(!casted)
return false;
248 if(_fixedPointConverter != casted->_fixedPointConverter)
return false;
249 return _target->mayReplaceOther(casted->_target);
261 template<
typename TargetUserType>
271 template<
typename COOKED_TYPE>
273 std::vector<int32_t> rawVector(1);
274 std::vector<COOKED_TYPE> cookedVector(1);
275 rawVector[0] = buffer_2D[channel][sample];
276 _fixedPointConverter.template vectorToCooked<COOKED_TYPE>(
277 rawVector.begin(), rawVector.end(), cookedVector.begin());
278 return cookedVector[0];
281 template<
typename COOKED_TYPE>
283 buffer_2D[channel][sample] = _fixedPointConverter.toRaw<COOKED_TYPE>(value);
287 const boost::shared_ptr<ChimeraTK::TransferElement const>& other)
const override {
288 auto casted = boost::dynamic_pointer_cast<FixedPointConvertingRawDecorator<TargetUserType>
const>(other);
289 if(!casted)
return false;
290 if(_fixedPointConverter != casted->_fixedPointConverter)
return false;
291 return _target->mayReplaceOther(casted->_target);
308 template<
typename UserType>
309 boost::shared_ptr<NDRegisterAccessor<UserType>> SubdeviceBackend::getRegisterAccessor_impl(
311 obtainTargetBackend();
312 boost::shared_ptr<NDRegisterAccessor<UserType>> returnValue;
313 if(type == Type::area) {
314 returnValue = getRegisterAccessor_area<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
316 else if(type == Type::threeRegisters || type == Type::twoRegisters || type == Type::areaHandshake) {
318 getRegisterAccessor_synchronized<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
323 returnValue->setExceptionBackend(shared_from_this());
331 size_t wordOffsetInRegister,
bool forceAlignment) {
336 std::cout <<
"SubdeviceBackend: WARNING: BAR others then 0 detected. BAR 0 will be used instead. Register "
347 size_t byteOffset = info.
address +
sizeof(int32_t) * wordOffsetInRegister;
348 if(forceAlignment && (byteOffset % 4 != 0)) {
350 "multiple of 4 are supported.");
354 if(numberOfWords == 0) {
357 else if(numberOfWords > info.
nElements) {
359 " elements from register '" + info.
pathName +
"', which only has a length of " +
365 if(numberOfWords + wordOffsetInRegister > info.
nElements) {
367 "SubdeviceBackend: Requested offset + number of words exceeds the size of the register '" + info.
pathName +
374 template<
typename UserType>
375 boost::shared_ptr<NDRegisterAccessor<UserType>> SubdeviceBackend::getRegisterAccessor_area(
377 assert(type == Type::area);
380 auto info = _registerMap.getBackendRegister(registerPathName);
381 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister,
true);
384 bool isRaw = flags.
has(AccessMode::raw);
387 size_t wordOffset = (info.address +
sizeof(int32_t) * wordOffsetInRegister) / 4;
388 flags.
add(AccessMode::raw);
389 auto rawAcc = targetDevice->getRegisterAccessor<int32_t>(targetArea, numberOfWords, wordOffset, flags);
394 return boost::make_shared<FixedPointConvertingDecorator<UserType, int32_t>>(rawAcc,
395 FixedPointConverter(registerPathName, info.channels.front().width, info.channels.front().nFractionalBits,
396 info.channels.front().signedFlag));
399 throw ChimeraTK::logic_error(
"Given UserType when obtaining the SubdeviceBackend in raw mode does not "s +
400 "match the expected type. Use an int32_t instead! (Register name: " + registerPathName +
"')");
405 boost::shared_ptr<SubdeviceRegisterAccessor> SubdeviceBackend::getRegisterAccessor_helper(
410 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister,
false);
415 "2reg or areaHandshake must have writeable registers only!");
419 boost::shared_ptr<NDRegisterAccessor<int32_t>> accAddress, accData;
420 if(!needAreaParam()) {
421 accAddress = targetDevice->getRegisterAccessor<int32_t>(targetAddress, 1, 0, {});
422 accData = targetDevice->getRegisterAccessor<int32_t>(targetData, 0, 0, {});
426 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister,
true);
429 size_t wordOffset = (info.
address +
sizeof(int32_t) * wordOffsetInRegister) / 4;
430 flags.
add(AccessMode::raw);
431 accData = targetDevice->getRegisterAccessor<int32_t>(targetArea, numberOfWords, wordOffset, flags);
433 boost::shared_ptr<NDRegisterAccessor<int32_t>> accStatus;
434 if(needStatusParam()) {
435 accStatus = targetDevice->getRegisterAccessor<int32_t>(targetControl, 1, 0, {});
438 size_t byteOffset = info.
address +
sizeof(int32_t) * wordOffsetInRegister;
439 auto sharedThis = boost::enable_shared_from_this<DeviceBackend>::shared_from_this();
441 return boost::make_shared<SubdeviceRegisterAccessor>(boost::dynamic_pointer_cast<SubdeviceBackend>(sharedThis),
442 info.
pathName, accAddress, accData, accStatus, byteOffset, numberOfWords);
447 template<
typename UserType>
448 boost::shared_ptr<NDRegisterAccessor<UserType>> SubdeviceBackend::getRegisterAccessor_synchronized(
449 const RegisterPath& registerPathName,
size_t numberOfWords,
size_t wordOffsetInRegister,
451 auto info = _registerMap.getBackendRegister(registerPathName);
452 boost::shared_ptr<SubdeviceRegisterAccessor> rawAcc =
453 getRegisterAccessor_helper(info, numberOfWords, wordOffsetInRegister, flags);
457 if(!flags.
has(AccessMode::raw)) {
458 return boost::make_shared<FixedPointConvertingDecorator<UserType, int32_t>>(rawAcc,
459 FixedPointConverter(registerPathName, info.channels.front().width, info.channels.front().nFractionalBits,
460 info.channels.front().signedFlag));
463 throw ChimeraTK::logic_error(
"Given UserType when obtaining the SubdeviceBackend in raw mode does not "s +
464 "match the expected type. Use an int32_t instead! (Register name: " + registerPathName +
"')");
470 boost::shared_ptr<NDRegisterAccessor<int32_t>> SubdeviceBackend::getRegisterAccessor_area<int32_t>(
472 assert(type == Type::area);
475 auto info = _registerMap.getBackendRegister(registerPathName);
476 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister,
true);
479 bool isRaw = flags.
has(AccessMode::raw);
482 size_t wordOffset = (info.address +
sizeof(int32_t) * wordOffsetInRegister) / 4;
483 flags.
add(AccessMode::raw);
484 auto rawAcc = targetDevice->getRegisterAccessor<int32_t>(targetArea, numberOfWords, wordOffset, flags);
489 return boost::make_shared<FixedPointConvertingDecorator<int32_t, int32_t>>(rawAcc,
490 FixedPointConverter(registerPathName, info.channels.front().width, info.channels.front().nFractionalBits,
491 info.channels.front().signedFlag));
493 return boost::make_shared<FixedPointConvertingRawDecorator<int32_t>>(rawAcc,
494 FixedPointConverter(registerPathName, info.channels.front().width, info.channels.front().nFractionalBits,
495 info.channels.front().signedFlag));
501 boost::shared_ptr<NDRegisterAccessor<int32_t>> SubdeviceBackend::getRegisterAccessor_synchronized<int32_t>(
502 const RegisterPath& registerPathName,
size_t numberOfWords,
size_t wordOffsetInRegister,
504 auto info = _registerMap.getBackendRegister(registerPathName);
505 boost::shared_ptr<SubdeviceRegisterAccessor> rawAcc =
506 getRegisterAccessor_helper(info, numberOfWords, wordOffsetInRegister, flags);
510 if(!flags.
has(AccessMode::raw)) {
511 return boost::make_shared<FixedPointConvertingDecorator<int32_t, int32_t>>(rawAcc,
512 FixedPointConverter(registerPathName, info.channels.front().width, info.channels.front().nFractionalBits,
513 info.channels.front().signedFlag));
515 return boost::make_shared<FixedPointConvertingRawDecorator<int32_t>>(rawAcc,
516 FixedPointConverter(registerPathName, info.channels.front().width, info.channels.front().nFractionalBits,
517 info.channels.front().signedFlag));
522 void SubdeviceBackend::setExceptionImpl() noexcept {
523 obtainTargetBackend();
524 targetDevice->setException(getActiveExceptionMessage());
529 void SubdeviceBackend::activateAsyncRead() noexcept {
530 obtainTargetBackend();
531 targetDevice->activateAsyncRead();