Directory: | ./ |
---|---|
File: | include/pdcom5/Variable.h |
Date: | 2024-11-05 15:23:15 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 20 | 20 | 100.0% |
Branches: | 3 | 6 | 50.0% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /***************************************************************************** | ||
2 | * vim:tw=78 | ||
3 | * | ||
4 | * Copyright (C) 2021 Richard Hacker (lerichi at gmx dot net), | ||
5 | * Florian Pose (fp at igh dot de), | ||
6 | * Bjarne von Horn (vh at igh dot de). | ||
7 | * | ||
8 | * This file is part of the PdCom library. | ||
9 | * | ||
10 | * The PdCom library is free software: you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU Lesser General Public License as published by | ||
12 | * the Free Software Foundation, either version 3 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * The PdCom library is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
17 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | ||
18 | * License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU Lesser General Public License | ||
21 | * along with the PdCom library. If not, see <http://www.gnu.org/licenses/>. | ||
22 | * | ||
23 | *****************************************************************************/ | ||
24 | |||
25 | /** @file */ | ||
26 | |||
27 | #ifndef PDCOM5_VARIABLE_H | ||
28 | #define PDCOM5_VARIABLE_H | ||
29 | |||
30 | #include "DataDeserializer.h" | ||
31 | #include "Future.h" | ||
32 | #include "Selector.h" | ||
33 | #include "SizeTypeInfo.h" | ||
34 | #include "details.h" | ||
35 | |||
36 | #include <chrono> | ||
37 | #include <istream> | ||
38 | #include <memory> | ||
39 | #include <ostream> | ||
40 | #include <pdcom5_export.h> | ||
41 | #include <stdint.h> | ||
42 | #include <string> | ||
43 | #include <vector> | ||
44 | |||
45 | namespace PdCom { | ||
46 | namespace impl { | ||
47 | class Variable; | ||
48 | } | ||
49 | struct Exception; | ||
50 | class Process; | ||
51 | class VariablePollResult; | ||
52 | |||
53 | /** PdCom Variable interface. | ||
54 | * | ||
55 | * This class represents a variable (signal or parameter). It contains | ||
56 | * information about the datatype and the shape of a variable. | ||
57 | * | ||
58 | * If you default-construct a Variable instance, it does not contain any | ||
59 | * information. Calling any other member function than empty() is forbidden. To | ||
60 | * get a valid variable instance, call Process::find(). | ||
61 | * | ||
62 | * setValue() returns a Future, which takes up to two callbacks to notify about | ||
63 | * the parameter update operation. If the server does not support these | ||
64 | * notifications, the Future is empty and no callbacks can be passed to it. | ||
65 | * | ||
66 | */ | ||
67 | 354 | class PDCOM5_PUBLIC Variable | |
68 | { | ||
69 | friend class impl::Variable; | ||
70 | |||
71 | 156 | explicit Variable(std::weak_ptr<const impl::Variable> pimpl) : | |
72 | 156 | pimpl_(std::move(pimpl)) | |
73 | 156 | {} | |
74 | |||
75 | public: | ||
76 | using PollFuture = | ||
77 | Future<PdCom::Exception const &, | ||
78 | VariablePollResult, | ||
79 | std::chrono::nanoseconds>; | ||
80 | |||
81 | using SetValueFuture = Future<PdCom::Exception const &>; | ||
82 | |||
83 | |||
84 | /** Constructs an empty variable. | ||
85 | */ | ||
86 | 2 | Variable() = default; | |
87 | |||
88 | /** Write to a variable. | ||
89 | * | ||
90 | * This function is also asynchronous, so the server might still use the old | ||
91 | * value when this function returns. This overload is for any container of | ||
92 | * arithmetic types (e.g. std::vector<int>). | ||
93 | * | ||
94 | * \c T must be a contiguous container (must have a size() method and a | ||
95 | * value_type typedef). | ||
96 | * | ||
97 | * \param data Data. | ||
98 | * \param selector Optional selector. | ||
99 | * | ||
100 | * \return empty Future if server does not support variable update feedback. | ||
101 | */ | ||
102 | template <typename T> | ||
103 | typename std::enable_if<!std::is_arithmetic<T>::value, SetValueFuture>::type | ||
104 | setValue(T const &data, const Selector &selector = {nullptr}) const | ||
105 | { | ||
106 | static_assert( | ||
107 | std::is_same< | ||
108 | decltype(data[0]), | ||
109 | typename std::add_lvalue_reference< | ||
110 | const typename T::value_type>::type>::value, | ||
111 | "Index operator does not return a lvalue reference of an " | ||
112 | "integral"); | ||
113 | static_assert( | ||
114 | details::is_contiguous<T>::value, | ||
115 | "Container must be contiguous"); | ||
116 | return setValue( | ||
117 | &data[0], | ||
118 | details::TypeInfoTraits<typename T::value_type>::type_info.type, | ||
119 | data.size(), selector); | ||
120 | } | ||
121 | /** Write to a variable. | ||
122 | * | ||
123 | * This function is also asynchronous, so the server might still use the old | ||
124 | * value when this function returns. | ||
125 | * This overload is for any arithmetic type (int, double, etc.). | ||
126 | * The variable must not be empty! | ||
127 | * | ||
128 | * \param data Data. | ||
129 | * \param selector Optional selector. | ||
130 | * | ||
131 | * \return empty Future if server does not support variable update feedback. | ||
132 | */ | ||
133 | template <typename T> | ||
134 | typename std::enable_if<std::is_arithmetic<T>::value, SetValueFuture>::type | ||
135 | 20 | setValue(T const &data, const Selector &selector = {nullptr}) const | |
136 | { | ||
137 | return setValue( | ||
138 | 20 | &data, details::TypeInfoTraits<T>::type_info.type, 1, selector); | |
139 | } | ||
140 | |||
141 | /** Write to a variable. | ||
142 | * | ||
143 | * This function is also asynchronous, so the server might still use the old | ||
144 | * value when this function returns. | ||
145 | * This overload is for any arithmetic type (int, double, etc.). | ||
146 | * The variable must not be empty! | ||
147 | * | ||
148 | * \param data Data. | ||
149 | * \param selector Optional selector. | ||
150 | * | ||
151 | * \return empty Future if server does not support variable update feedback. | ||
152 | */ | ||
153 | template <typename T, size_t M, size_t N> | ||
154 | SetValueFuture | ||
155 | 1 | setValue(const T (&data)[M][N], const Selector &selector = {nullptr}) const | |
156 | { | ||
157 | return setValue( | ||
158 | 1 | data, details::TypeInfoTraits<T>::type_info.type, M * N, | |
159 | 1 | selector); | |
160 | } | ||
161 | |||
162 | /** Write to a variable. | ||
163 | * | ||
164 | * This function is also asynchronous, so the server might still use the old | ||
165 | * value when this function returns. | ||
166 | * The variable must not be empty! | ||
167 | * | ||
168 | * \param src Pointer to one or more values. | ||
169 | * \param src_type Type of the source. | ||
170 | * \param count Number of values. | ||
171 | * \param selector Optional selector. | ||
172 | * | ||
173 | * \return empty Future if server does not support variable update feedback. | ||
174 | */ | ||
175 | SetValueFuture setValue( | ||
176 | const void *src, | ||
177 | TypeInfo::DataType src_type, | ||
178 | size_t count, | ||
179 | const Selector &selector = {nullptr}) const; | ||
180 | |||
181 | /** Write to a variable. | ||
182 | * | ||
183 | * This function is also asynchronous, so the server might still use the old | ||
184 | * value when this function returns. | ||
185 | * The variable must not be empty! | ||
186 | * | ||
187 | * \param src Pointer to one or more values. | ||
188 | * \param src_type Type of the source. | ||
189 | * \param count Number of values. | ||
190 | * \param offset Optional offset in the destination. | ||
191 | * | ||
192 | * \return empty Future if server does not support variable update feedback. | ||
193 | */ | ||
194 | PdCom::Variable::SetValueFuture setValue( | ||
195 | const void *src, | ||
196 | TypeInfo::DataType src_type, | ||
197 | size_t count, | ||
198 | size_t offset) const; | ||
199 | |||
200 | /** Get details about the variable type. | ||
201 | * The variable must not be empty! | ||
202 | * @return Variable type info. | ||
203 | */ | ||
204 | TypeInfo getTypeInfo() const; | ||
205 | /** Get details about the variable shape. | ||
206 | * The variable must not be empty! | ||
207 | * @return Variable shape info. | ||
208 | */ | ||
209 | SizeInfo getSizeInfo() const; | ||
210 | /** The Path of the variable. | ||
211 | * The variable must not be empty! | ||
212 | * @return The full path. | ||
213 | */ | ||
214 | std::string getPath() const; | ||
215 | /** The name of the variable. | ||
216 | * The variable must not be empty! | ||
217 | * @return the name, without the path components. | ||
218 | */ | ||
219 | std::string getName() const; | ||
220 | /** The alias of the variable, if set. | ||
221 | * The variable must not be empty! | ||
222 | * @return the alias. | ||
223 | */ | ||
224 | std::string getAlias() const; | ||
225 | /** The task id of the variable. | ||
226 | * The variable must not be empty! | ||
227 | * @return the task id. | ||
228 | */ | ||
229 | int getTaskId() const; | ||
230 | |||
231 | /** @return whether the variable is writeable. | ||
232 | */ | ||
233 | bool isWriteable() const; | ||
234 | /** @return sample time, 0 for parameters. | ||
235 | */ | ||
236 | std::chrono::duration<double> getSampleTime() const; | ||
237 | |||
238 | |||
239 | /** Checks whether this instance is empty. | ||
240 | * | ||
241 | * Default-constructed variables are empty per default, | ||
242 | * calling any other member function than empty() will crash. | ||
243 | * \return false if this instance is alive. | ||
244 | */ | ||
245 | 54 | bool empty() const noexcept { return (pimpl_.expired()); } | |
246 | |||
247 | /** Read a variable without subscription. | ||
248 | * | ||
249 | * This method returns a Future. To get the actual result, | ||
250 | * set a callback function using Future::then(). | ||
251 | * You can use Lambdas to capture local variables in the callback. | ||
252 | * The callback gets two arguments: a VariablePollResult instance and a | ||
253 | * timestamp. Make sure to keep the Future until it is resolved, otherwise | ||
254 | * the poll request will be dropped. So this is not fire-and-forget. | ||
255 | * | ||
256 | * \code | ||
257 | * auto handle = ...; | ||
258 | * const auto future = var.poll().then([&handle](VariablePollResult res, | ||
259 | * std::chrono::nanoseconds ts) | ||
260 | * { | ||
261 | * double val; | ||
262 | * res.getValue(val); | ||
263 | * handle.processValue(val); | ||
264 | * }); | ||
265 | * \endcode | ||
266 | * | ||
267 | */ | ||
268 | PollFuture poll() const; | ||
269 | |||
270 | /** Get the assigned Process. | ||
271 | * \return The Process. | ||
272 | */ | ||
273 | Process *getProcess() const; | ||
274 | |||
275 | private: | ||
276 | std::weak_ptr<const impl::Variable> pimpl_; | ||
277 | }; | ||
278 | |||
279 | /** Result of Variable::poll() | ||
280 | * | ||
281 | * This is a container for a Variable value. | ||
282 | * The data can be extracted with one of the many getValue() overloads. | ||
283 | * | ||
284 | */ | ||
285 | 5 | class PDCOM5_PUBLIC VariablePollResult : | |
286 | public DataDeserializer<VariablePollResult> | ||
287 | { | ||
288 | std::vector<char> data_; | ||
289 | Variable variable_; | ||
290 | |||
291 | public: | ||
292 | 1 | explicit VariablePollResult(Variable var) : | |
293 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | data_(var.getSizeInfo().totalElements() |
294 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | * var.getTypeInfo().element_size), |
295 |
1/2✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
4 | variable_(var) |
296 | 1 | {} | |
297 | |||
298 | 1 | const void *getData() const noexcept { return data_.data(); } | |
299 | 1 | void *getData() noexcept { return data_.data(); } | |
300 | 1 | Variable getVariable() const noexcept { return variable_; } | |
301 | }; | ||
302 | |||
303 | } // namespace PdCom | ||
304 | |||
305 | #endif // PDCOM5_VARIABLE_H | ||
306 |