ChimeraTK-ApplicationCore 04.08.00
Loading...
Searching...
No Matches
PyScalarAccessor.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
4#include "PyScalarAccessor.h"
5
6#include <pybind11/stl.h>
7
8namespace py = pybind11;
9
10namespace ChimeraTK {
11
12 /********************************************************************************************************************/
13
14 template<template<typename> class AccessorType>
15 UserTypeTemplateVariantNoVoid<ScalarAccessor> PyScalarAccessor::createAccessor(ChimeraTK::DataType type,
16 Module* owner, const std::string& name, const std::string& unit, const std::string& description,
17 const std::unordered_set<std::string>& tags) {
18 std::optional<UserTypeTemplateVariantNoVoid<ScalarAccessor>> rv;
19 ChimeraTK::callForTypeNoVoid(type, [&](auto t) {
20 using UserType = decltype(t);
21 AccessorType<UserType> acc(owner, name, unit, description, tags);
22 rv.emplace(std::in_place_type<ScalarAccessor<UserType>>, std::move(acc));
23 });
24 return std::move(rv.value());
25 }
26
27 /********************************************************************************************************************/
28
30
31 /********************************************************************************************************************/
32
33 template<template<typename> class AccessorType>
35 const std::string& name, const std::string& unit, const std::string& description,
36 const std::unordered_set<std::string>& tags)
37 : _accessor(createAccessor<AccessorType>(type, owner, name, unit, description, tags)) {}
38
39 /********************************************************************************************************************/
40
42
43 /********************************************************************************************************************/
44
45 UserTypeVariantNoVoid PyScalarAccessor::readAndGet() {
46 std::optional<UserTypeVariantNoVoid> rv;
47 py::gil_scoped_release release;
48 std::visit([&](auto& acc) { rv = acc.readAndGet(); }, _accessor);
49 return rv.value();
50 }
51
52 /********************************************************************************************************************/
53
54 UserTypeVariantNoVoid PyScalarAccessor::get() const {
55 std::optional<UserTypeVariantNoVoid> rv;
56 std::visit(
57 [&](auto& acc) {
58 using ACC = typename std::remove_reference<decltype(acc)>::type;
59 using expectedUserType = typename ACC::value_type;
60 rv = expectedUserType(
61 boost::dynamic_pointer_cast<NDRegisterAccessor<expectedUserType>>(acc.getHighLevelImplElement())
62 ->accessData(0));
63 },
64 _accessor);
65 return rv.value();
66 }
67
68 /********************************************************************************************************************/
69
70 void PyScalarAccessor::writeIfDifferent(UserTypeVariantNoVoid val) {
71 std::visit(
72 [&](auto& acc) {
73 using ACC = typename std::remove_reference<decltype(acc)>::type;
74 using expectedUserType = typename ACC::value_type;
75 std::visit(
76 [&](auto value) { acc.writeIfDifferent(ChimeraTK::userTypeToUserType<expectedUserType>(value)); }, val);
77 },
78 _accessor);
79 }
80
81 /********************************************************************************************************************/
82
83 void PyScalarAccessor::setAndWrite(UserTypeVariantNoVoid val) {
84 std::visit(
85 [&](auto& acc) {
86 using ACC = typename std::remove_reference<decltype(acc)>::type;
87 using expectedUserType = typename ACC::value_type;
88 std::visit([&](auto value) { acc.setAndWrite(ChimeraTK::userTypeToUserType<expectedUserType>(value)); }, val);
89 },
90 _accessor);
91 }
92
93 /********************************************************************************************************************/
94
95 void PyScalarAccessor::set(UserTypeVariantNoVoid val) {
96 std::visit(
97 [&](auto& acc) {
98 using ACC = typename std::remove_reference<decltype(acc)>::type;
99 using expectedUserType = typename ACC::value_type;
100 std::visit([&](auto value) { acc = ChimeraTK::userTypeToUserType<expectedUserType>(value); }, val);
101 },
102 _accessor);
103 }
104
105 /********************************************************************************************************************/
106
107 std::string PyScalarAccessor::repr(py::object& acc) {
108 if(not acc.cast<PyScalarAccessor&>().getTE().isInitialised()) {
109 return "<ScalarAccessor(not initialized)>";
110 }
111
112 std::string rep{"<ScalarAccessor(type="};
113 rep.append(py::cast<py::object>(py::cast(&acc).attr("getValueType")()).attr("__repr__")().cast<std::string>());
114 rep.append(", name=");
115 rep.append(py::cast(&acc).attr("getName")().cast<std::string>());
116 rep.append(", data=");
117 rep.append(py::cast<py::object>(py::cast(&acc).attr("__str__")()).cast<std::string>());
118 rep.append(", versionNumber=");
119 rep.append(py::cast<py::object>(py::cast(&acc).attr("getVersionNumber")()).attr("__repr__")().cast<std::string>());
120 rep.append(", dataValidity=");
121 rep.append(py::cast<py::object>(py::cast(&acc).attr("dataValidity")()).attr("__repr__")().cast<std::string>());
122 rep.append(")>");
123 return rep;
124 }
125
126 /********************************************************************************************************************/
127
129 // strictly speaking, py::nodelete is not necessary since we hand out instances only via factory function,
130 // but leave it here for consistency
131 py::class_<PyScalarAccessor, PyTransferElementBase, std::unique_ptr<PyScalarAccessor, py::nodelete>> scalaracc(
132 m, "ScalarAccessor", py::buffer_protocol(), py::module_local());
133 scalaracc.def(py::init<>())
134 .def("read", &PyScalarAccessor::read,
135 "Read the data from the device.\n\nIf AccessMode::wait_for_new_data was set, this function will block "
136 "until new data has arrived. Otherwise it still might block for a short time until the data transfer was "
137 "complete.")
138 .def("readNonBlocking", &PyScalarAccessor::readNonBlocking,
139 "Read the next value, if available in the input buffer.\n\nIf AccessMode::wait_for_new_data was set, this "
140 "function returns immediately and the return value indicated if a new value was available (true) or not "
141 "(false).\n\nIf AccessMode::wait_for_new_data was not set, this function is identical to read(), which "
142 "will still return quickly. Depending on the actual transfer implementation, the backend might need to "
143 "transfer data to obtain the current value before returning. Also this function is not guaranteed to be "
144 "lock free. The return value will be always true in this mode.")
145 .def("readLatest", &PyScalarAccessor::readLatest,
146 "Read the latest value, discarding any other update since the last read if present.\n\nOtherwise this "
147 "function is identical to readNonBlocking(), i.e. it will never wait for new values and it will return "
148 "whether a new value was available if AccessMode::wait_for_new_data is set.")
149 .def("getName", &PyScalarAccessor::getName, "Returns the name that identifies the process variable.")
150 .def("getUnit", &PyScalarAccessor::getUnit,
151 "Returns the engineering unit.\n\nIf none was specified, it will default to ' n./ a.'")
152 .def("getDescription", &PyScalarAccessor::getDescription, "Returns the description of this variable/register.")
153 .def("getValueType", &PyScalarAccessor::getValueType,
154 "Returns the std::type_info for the value type of this transfer element.\n\nThis can be used to determine "
155 "the type at runtime.")
156 .def("getVersionNumber", &PyScalarAccessor::getVersionNumber,
157 "Returns the version number that is associated with the last transfer (i.e. last read or write)")
158 .def("isReadOnly", &PyScalarAccessor::isReadOnly,
159 "Check if transfer element is read only, i.e. it is readable but not writeable.")
160 .def("isReadable", &PyScalarAccessor::isReadable, "Check if transfer element is readable.")
161 .def("isWriteable", &PyScalarAccessor::isWriteable, "Check if transfer element is writeable.")
162 .def("getId", &PyScalarAccessor::getId,
163 "Obtain unique ID for the actual implementation of this TransferElement.\n\nThis means that e.g. two "
164 "instances of ScalarRegisterAccessor created by the same call to Device::getScalarRegisterAccessor() (e.g. "
165 "by copying the accessor to another using NDRegisterAccessorBridge::replace()) will have the same ID, "
166 "while two instances obtained by to difference calls to Device::getScalarRegisterAccessor() will have a "
167 "different ID even when accessing the very same register.")
168 .def("dataValidity", &PyScalarAccessor::dataValidity,
169 "Return current validity of the data.\n\nWill always return DataValidity.ok if the backend does not "
170 "support it")
171 .def("get", &PyScalarAccessor::get, "Return a value of UserType (without a previous read).")
172 .def(
173 "readAndGet", &PyScalarAccessor::readAndGet, "Convenience function to read and return a value of UserType.")
174 .def("set", &PyScalarAccessor::set, "Set the value of UserType.", py::arg("val"))
175 .def("write", &PyScalarAccessor::write,
176 "Write the data to device.\n\nThe return value is true, old data was lost on the write transfer (e.g. due "
177 "to an buffer overflow). In case of an unbuffered write transfer, the return value will always be false.")
178 .def("writeDestructively", &PyScalarAccessor::writeDestructively,
179 "Just like write(), but allows the implementation to destroy the content of the user buffer in the "
180 "process.\n\nThis is an optional optimisation, hence there is a default implementation which just calls "
181 "the normal doWriteTransfer(). In any case, the application must expect the user buffer of the "
182 "TransferElement to contain undefined data after calling this function.")
183 .def("writeIfDifferent", &PyScalarAccessor::writeIfDifferent,
184 "Convenience function to set and write new value if it differes from the current value.\n\nThe given "
185 "version number is only used in case the value differs. If versionNumber == {nullptr}, a new version "
186 "number is generated only if the write actually takes place.",
187 py::arg("val"))
188 .def("setAndWrite", &PyScalarAccessor::setAndWrite,
189 "Convenience function to set and write new value.\n\nThe given version number. If versionNumber == {}, a "
190 "new version number is generated.",
191 py::arg("val"))
192 .def("__repr__", &PyScalarAccessor::repr);
193
195 scalaracc.def(fn.c_str(), [fn](PyScalarAccessor& acc, PyScalarAccessor& other) {
196 return py::cast<py::object>(py::cast(acc).attr("get")()).attr(fn.c_str())(py::cast(other).attr("get")());
197 });
198 scalaracc.def(fn.c_str(), [fn](PyScalarAccessor& acc, py::object& other) {
199 return py::cast<py::object>(py::cast(acc).attr("get")()).attr(fn.c_str())(other);
200 });
201 }
202
204 scalaracc.def(fn.c_str(),
205 [fn](PyScalarAccessor& acc) { return py::cast<py::object>(py::cast(acc).attr("get")()).attr(fn.c_str())(); });
206 }
207
211 m.def(
212 "ScalarPushInput",
213 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
214 const std::string& description, const std::unordered_set<std::string>& tags) {
215 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
216 AccessorTypeTag<ScalarPushInput>{}, type, &owner, name, unit, description, tags);
217 },
218 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
219 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
220 m.def(
221 "ScalarPushInput",
222 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
223 const std::string& description, const std::unordered_set<std::string>& tags) {
224 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
225 AccessorTypeTag<ScalarPushInput>{}, DataType(type), &owner, name, unit, description, tags);
226 },
227 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
228 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
229
233 m.def(
234 "ScalarPushInputWB",
235 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
236 const std::string& description, const std::unordered_set<std::string>& tags) {
237 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
238 AccessorTypeTag<ScalarPushInputWB>{}, type, &owner, name, unit, description, tags);
239 },
240 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
241 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
242 m.def(
243 "ScalarPushInputWB",
244 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
245 const std::string& description, const std::unordered_set<std::string>& tags) {
246 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
247 AccessorTypeTag<ScalarPushInputWB>{}, DataType(type), &owner, name, unit, description, tags);
248 },
249 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
250 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
251
255 m.def(
256 "ScalarPollInput",
257 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
258 const std::string& description, const std::unordered_set<std::string>& tags) {
259 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
260 AccessorTypeTag<ScalarPollInput>{}, type, &owner, name, unit, description, tags);
261 },
262 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
263 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
264 m.def(
265 "ScalarPollInput",
266 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
267 const std::string& description, const std::unordered_set<std::string>& tags) {
268 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
269 AccessorTypeTag<ScalarPollInput>{}, DataType(type), &owner, name, unit, description, tags);
270 },
271 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
272 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
273
277 m.def(
278 "ScalarOutput",
279 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
280 const std::string& description, const std::unordered_set<std::string>& tags) {
281 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
282 AccessorTypeTag<ScalarOutput>{}, type, &owner, name, unit, description, tags);
283 },
284 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
285 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
286 m.def(
287 "ScalarOutput",
288 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
289 const std::string& description, const std::unordered_set<std::string>& tags) {
290 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
291 AccessorTypeTag<ScalarOutput>{}, DataType(type), &owner, name, unit, description, tags);
292 },
293 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
294 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
295
299 m.def(
300 "ScalarOutputPushRB",
301 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
302 const std::string& description, const std::unordered_set<std::string>& tags) {
303 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
304 AccessorTypeTag<ScalarOutputPushRB>{}, type, &owner, name, unit, description, tags);
305 },
306 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
307 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
308 m.def(
309 "ScalarOutputPushRB",
310 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
311 const std::string& description, const std::unordered_set<std::string>& tags) {
312 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
313 AccessorTypeTag<ScalarOutputPushRB>{}, DataType(type), &owner, name, unit, description, tags);
314 },
315 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
316 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
317
321 m.def(
322 "ScalarOutputReverseRecovery",
323 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
324 const std::string& description, const std::unordered_set<std::string>& tags) {
325 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
326 AccessorTypeTag<ScalarOutputReverseRecovery>{}, type, &owner, name, unit, description, tags);
327 },
328 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
329 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
330 m.def(
331 "ScalarOutputReverseRecovery",
332 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
333 const std::string& description, const std::unordered_set<std::string>& tags) {
334 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
335 AccessorTypeTag<ScalarOutputReverseRecovery>{}, DataType(type), &owner, name, unit, description, tags);
336 },
337 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("description"),
338 py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference, "");
339 }
340
341 /********************************************************************************************************************/
342
343} // namespace ChimeraTK
Base class for ApplicationModule and DeviceModule, to have a common interface for these module types.
Definition Module.h:21
Base class used for all objects in the Python world which can own other objects and can be owned them...
void set(UserTypeVariantNoVoid val)
void writeIfDifferent(UserTypeVariantNoVoid val)
static std::string repr(py::object &acc)
UserTypeVariantNoVoid readAndGet()
static void bind(py::module &mod)
void setAndWrite(UserTypeVariantNoVoid val)
UserTypeTemplateVariantNoVoid< ScalarAccessor > _accessor
UserTypeVariantNoVoid get() const
static const std::set< std::string > specialUnaryFunctionsToEmulateNumeric
static const std::set< std::string > specialFunctionsToEmulateNumeric
const TransferElementAbstractor & getTE() const final
Convenience class for output scalar accessors (always UpdateMode::push)
InvalidityTracer application module.
module_ module