GCC Code Coverage Report


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