| Directory: | ./ |
|---|---|
| File: | include/pdcom5/Variable.h |
| Date: | 2025-02-12 12:41:42 |
| 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 |