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