ChimeraTK-DeviceAccess-TangoBackend 00.01.02
Loading...
Searching...
No Matches
TangoRegisterAccessor.h
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#pragma once
4
5#include "TangoBackend.h"
6
7#include <ChimeraTK/Exception.h>
8#include <ChimeraTK/NDRegisterAccessor.h>
9#include <ChimeraTK/TransferElement.h>
10
11#include <omniORB4/CORBA.h>
12#include <tango/idl/tango.h>
13#include <tango/tango.h>
14
15#include <iterator>
16#include <utility>
17
18namespace ChimeraTK {
19
20 template<typename UserType, typename TangoType>
21 class TangoBackendRegisterAccessor : public NDRegisterAccessor<UserType> {
22 public:
23 TangoBackendRegisterAccessor(boost::shared_ptr<TangoBackend> backend_, TangoRegisterInfo info,
24 const std::string& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags);
25
26 void doReadTransferSynchronously() override;
27 bool doWriteTransfer(VersionNumber) override;
28
29 void doPreRead(TransferType) override;
30 void doPostRead(TransferType type, bool hasNewData) override;
31 void doPreWrite(TransferType, VersionNumber) override;
32
33 std::vector<boost::shared_ptr<TransferElement>> getHardwareAccessingElements() override {
34 return {boost::enable_shared_from_this<TransferElement>::shared_from_this()};
35 }
36
37 std::list<boost::shared_ptr<TransferElement>> getInternalElements() override { return {}; }
38
39 [[nodiscard]] bool isReadOnly() const override { return isReadable() && !isWriteable(); }
40
41 [[nodiscard]] bool isWriteable() const override { return registerInfo.isWriteable(); }
42
43 [[nodiscard]] bool isReadable() const override { return registerInfo.isReadable(); }
44
46 boost::shared_ptr<TangoBackend> backend;
48
49 // This will be filled by the readTransfer to be processed in doPostRead
50 Tango::DeviceAttribute readAttribute;
51
52 // This will be filled by preWrite to be sent to the server in doWriteTransfer
53 Tango::DeviceAttribute writeAttribute;
54 bool isPartial{false};
55
57 };
58
59 /********************************************************************************************************************/
60 /********************************************************************************************************************/
61 /********************************************************************************************************************/
62
63 template<typename UserType, typename TangoType>
65 boost::shared_ptr<TangoBackend> backend_, TangoRegisterInfo info, const std::string& registerPathName,
66 size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
67 : NDRegisterAccessor<UserType>(registerPathName, flags), backend(std::move(backend_)), registerInfo(std::move(info)) {
68 auto actualLength = registerInfo.getNumberOfElements();
69 elementOffset = wordOffsetInRegister;
70 auto nElements = numberOfWords;
71
72 if(nElements == 0) {
73 nElements = actualLength;
74 }
75
76 if(flags.has(AccessMode::raw) || flags.has(AccessMode::wait_for_new_data)) {
77 throw ChimeraTK::logic_error("invalid access mode for this register");
78 }
79
80 if(nElements + elementOffset > actualLength) {
81 throw ChimeraTK::logic_error(
82 "Requested number of words exceeds the length of the Tango attribute " + registerPathName);
83 }
84
85 isPartial = nElements != actualLength || elementOffset != 0;
86
87 NDRegisterAccessor<UserType>::buffer_2D.resize(1);
88 NDRegisterAccessor<UserType>::buffer_2D[0].resize(nElements);
89 }
90
91 /********************************************************************************************************************/
92
93 template<typename UserType, typename TangoType>
95 if(!backend->isFunctional()) {
96 throw ChimeraTK::runtime_error(std::string("Exception reported by another accessor"));
97 }
98
99 try {
100 readAttribute = backend->getDeviceProxy()->read_attribute(registerInfo.attributeInfo.name);
101 }
102 catch(CORBA::Exception& ex) {
103 throw ChimeraTK::runtime_error(
104 "Failed to read from attribute " + registerInfo.attributeInfo.name + ": " + ex._name());
105 }
106 }
107
108 /********************************************************************************************************************/
109
110 template<typename UserType, typename TangoType>
112 if(!backend->isFunctional()) {
113 throw ChimeraTK::runtime_error(std::string("Exception reported by another accessor"));
114 }
115
116 try {
117 backend->getDeviceProxy()->write_attribute(writeAttribute);
118 }
119 catch(CORBA::Exception& ex) {
120 throw ChimeraTK::runtime_error(
121 "Failed to write to attribute " + registerInfo.attributeInfo.name + ": " + ex._name());
122 }
123
124 return false;
125 }
126
127 /********************************************************************************************************************/
128
129 template<typename UserType, typename TangoType>
130 void TangoBackendRegisterAccessor<UserType, TangoType>::doPreWrite(TransferType type, VersionNumber version) {
131 NDRegisterAccessor<UserType>::doPreWrite(type, version);
132 if(!backend->isOpen()) {
133 throw ChimeraTK::logic_error("Write operation not allowed while device is closed.");
134 }
135 if(!isWriteable()) {
136 throw ChimeraTK::logic_error("Try to write read-only register \"" + registerInfo.getRegisterName() + "\".");
137 }
138
139 std::vector<TangoType> value;
140
141 // Perform read-modify-write
142 if(isPartial) {
143 try {
144 auto attr = backend->getDeviceProxy()->read_attribute(registerInfo.attributeInfo.name);
145 attr >> value;
146 }
147 catch(CORBA::Exception& ex) {
148 throw ChimeraTK::runtime_error(
149 "Failure to read in read-modfiy-write of partial accessor " + registerInfo.getRegisterName());
150 }
151 }
152
153 // Some versions of Tango return one element too much, so we do this unconditionally, even for read-modify-write
154 value.resize(this->registerInfo.getNumberOfElements());
155
156 auto destinationStart = value.begin() + elementOffset;
157
158 if constexpr(std::is_same_v<TangoType, Tango::DevEnum>) {
159 std::transform(this->buffer_2D[0].begin(), this->buffer_2D[0].end(), destinationStart,
160 ChimeraTK::userTypeToNumeric<Tango::DevEnum, UserType>);
161 }
162 else if constexpr(std::is_same_v<TangoType, Tango::DevState>) {
163 // Technically we should never end up here, unless someone added an additional attribute
164 // That uses DevState and isn't the built-in "State" which is r/o
165 std::transform(this->buffer_2D[0].begin(), this->buffer_2D[0].end(), destinationStart,
166 [](UserType& v) { return static_cast<Tango::DevState>(ChimeraTK::userTypeToNumeric<int, UserType>(v)); });
167 }
168 else {
169 std::transform(this->buffer_2D[0].begin(), this->buffer_2D[0].end(), destinationStart,
170 ChimeraTK::userTypeToUserType<TangoType, UserType>);
171 }
172 this->writeAttribute = Tango::DeviceAttribute(this->registerInfo.attributeInfo.name, value);
173 }
174
175 /********************************************************************************************************************/
176
177 template<typename UserType, typename TangoType>
179 if(!backend->isOpen()) {
180 throw ChimeraTK::logic_error("Read operation not allowed while device is closed.");
181 }
182 if(!isReadable()) {
183 throw ChimeraTK::logic_error("Try to read from write-only register \"" + registerInfo.getRegisterName() + "\".");
184 }
185 }
186
187 /********************************************************************************************************************/
188
189 template<typename UserType, typename TangoType>
190 void TangoBackendRegisterAccessor<UserType, TangoType>::doPostRead(TransferType type, bool hasNewData) {
191 NDRegisterAccessor<UserType>::doPostRead(type, hasNewData);
192 if(!hasNewData) {
193 return;
194 }
195
196 // If the Attribute Quality is invalid, data extraction will not work. There is no data to read.
197 if(this->readAttribute.get_quality() != Tango::AttrQuality::ATTR_INVALID) {
198 std::vector<TangoType> value;
199 try {
200 this->readAttribute >> value;
201 }
202 catch(Tango::DevFailed& ex) {
203 throw ChimeraTK::runtime_error("Failed to read from attribute " + registerInfo.attributeInfo.name);
204 }
205
206 auto length = std::min(this->buffer_2D[0].size(), value.size());
207 auto sourceStart = value.begin() + elementOffset;
208 auto sourceEnd = sourceStart + length;
209 if constexpr(std::is_same_v<TangoType, Tango::DevEnum>) {
210 std::transform(
211 sourceStart, sourceEnd, this->buffer_2D[0].begin(), ChimeraTK::numericToUserType<UserType, Tango::DevEnum>);
212 }
213 if constexpr(std::is_same_v<TangoType, Tango::DevState>) {
214 std::transform(sourceStart, sourceEnd, this->buffer_2D[0].begin(),
215 [](TangoType& v) { return ChimeraTK::numericToUserType<UserType, int>(static_cast<int>(v)); });
216 }
217 else {
218 std::transform(
219 sourceStart, sourceEnd, this->buffer_2D[0].begin(), ChimeraTK::userTypeToUserType<UserType, TangoType>);
220 }
221 }
222
223 // FIXME: We currently have no means of correlating data from Tango, so everything gets a new version number
224 TransferElement::_versionNumber = {};
225
226 // FIXME: Not really sure if CHANGING is also valid, but it is definitely more valid than all the others.
227 TransferElement::_dataValidity = (this->readAttribute.quality == Tango::AttrQuality::ATTR_VALID ||
228 this->readAttribute.quality == Tango::AttrQuality::ATTR_CHANGING) ?
229 DataValidity::ok :
230 DataValidity::faulty;
231 }
232
233 /********************************************************************************************************************/
234
235} // namespace ChimeraTK
void doPostRead(TransferType type, bool hasNewData) override
boost::shared_ptr< TangoBackend > backend
Pointer to the backend.
TangoBackendRegisterAccessor(boost::shared_ptr< TangoBackend > backend_, TangoRegisterInfo info, const std::string &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
std::list< boost::shared_ptr< TransferElement > > getInternalElements() override
void doPreWrite(TransferType, VersionNumber) override
std::vector< boost::shared_ptr< TransferElement > > getHardwareAccessingElements() override
bool isReadable() const override
unsigned int getNumberOfElements() const override
bool isWriteable() const override