ChimeraTK-ApplicationCore 04.06.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());
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) {
215 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
216 AccessorTypeTag<ScalarPushInput>{}, type, &owner, name, unit, description);
217 },
218 py::return_value_policy::reference, "");
219
223 m.def(
224 "ScalarPushInputWB",
225 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
226 const std::string& description) {
227 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
228 AccessorTypeTag<ScalarPushInputWB>{}, type, &owner, name, unit, description);
229 },
230 py::return_value_policy::reference, "");
231
235 m.def(
236 "ScalarPollInput",
237 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
238 const std::string& description) {
239 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
240 AccessorTypeTag<ScalarPollInput>{}, type, &owner, name, unit, description);
241 },
242 py::return_value_policy::reference, "");
243
247 m.def(
248 "ScalarOutput",
249 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
250 const std::string& description) {
251 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
252 AccessorTypeTag<ScalarOutput>{}, type, &owner, name, unit, description);
253 },
254 py::return_value_policy::reference, "");
255
259 m.def(
260 "ScalarOutputPushRB",
261 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
262 const std::string& description) {
263 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
264 AccessorTypeTag<ScalarOutputPushRB>{}, type, &owner, name, unit, description);
265 },
266 py::return_value_policy::reference, "");
267
271 m.def(
272 "ScalarOutputReverseRecovery",
273 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
274 const std::string& description) {
275 return dynamic_cast<PyOwningObject&>(owner).make_child<PyScalarAccessor>(
276 AccessorTypeTag<ScalarOutputReverseRecovery>{}, type, &owner, name, unit, description);
277 },
278 py::return_value_policy::reference, "");
279 }
280
281 /********************************************************************************************************************/
282
283} // 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
InvalidityTracer application module.
module_ module
Convenience class for output scalar accessors (always UpdateMode::push)