55 * Systems, RWTH Aachen University SPDX-License-Identifier: Apache-2.0
66 */
77
8+ #include < cstdint>
9+ #include < optional>
10+ #include < stdexcept>
11+
812#include < jansson.h>
913#include < pybind11/pybind11.h>
14+ #include < pybind11/stl.h>
15+ #include < sys/types.h>
16+ #include < unistd.h>
1017#include < uuid/uuid.h>
1118
1219#include < villas/node.hpp>
1320#include < villas/sample.hpp>
21+ #include < villas/timing.hpp>
1422
1523extern " C" {
1624#include < villas/node.h>
1725}
1826
1927namespace py = pybind11;
2028
21- class Array {
29+ class SamplesArray {
2230public:
23- Array (unsigned int len) {
24- smps = new vsample *[len]();
31+ SamplesArray (unsigned int len = 0 ) {
32+ smps = (len > 0 ) ? new vsample *[len]() : nullptr ;
2533 this ->len = len;
2634 }
27- Array (const Array &) = delete ;
28- Array &operator =(const Array &) = delete ;
35+ SamplesArray (const SamplesArray &) = delete ;
36+ SamplesArray &operator =(const SamplesArray &) = delete ;
2937
30- ~Array () {
38+ ~SamplesArray () {
39+ if (!smps)
40+ return ;
3141 for (unsigned int i = 0 ; i < len; ++i) {
32- sample_decref (smps[i]);
33- smps[i] = nullptr ;
42+ if (smps[i]) {
43+ sample_decref (smps[i]);
44+ smps[i] = nullptr ;
45+ }
3446 }
3547 delete[] smps;
3648 }
@@ -47,9 +59,45 @@ class Array {
4759 }
4860 }
4961
50- vsample *&operator [](unsigned int idx) { return smps[idx]; }
62+ /*
63+ * Performs a resize of the underlying SamplesArray copying each Sample.
64+ * Shrinking has asymmetric behavior which may be undesired.
65+ * Therefore use clear().
66+ */
67+ int grow (unsigned int add) {
68+ unsigned int new_len = this ->len + add;
69+ vsample **smps_new = new vsample *[new_len]();
70+ for (unsigned int i = 0 ; i < this ->len ; ++i) {
71+ smps_new[i] = smps[i];
72+ }
73+ delete[] smps;
74+ this ->smps = smps_new;
75+ this ->len = new_len;
76+
77+ return new_len;
78+ }
79+
80+ int clear () {
81+ if (this ->smps ) {
82+ unsigned int i = 0 ;
83+ for (; i < len; ++i) {
84+ sample_decref (smps[i]);
85+ smps[i] = nullptr ;
86+ }
87+ delete[] smps;
88+ smps = nullptr ;
89+ this ->len = 0 ;
90+ return i;
91+ }
92+ return -1 ;
93+ }
94+
95+ vsample *&operator [](unsigned int idx) {
96+ vsample *&ref = smps[idx];
97+ return ref;
98+ }
5199
52- vsample *& operator [](unsigned int idx) const { return smps[idx]; }
100+ vsample *operator [](unsigned int idx) const { return smps[idx]; }
53101
54102 vsample **get_smps () { return smps; }
55103
@@ -60,8 +108,15 @@ class Array {
60108 unsigned int len;
61109};
62110
111+ struct timespec ns_to_timespec (int64_t time_ns) {
112+ struct timespec ts;
113+ ts.tv_nsec = time_ns / 1'000'000'000LL ;
114+ ts.tv_sec = time_ns % 1'000'000'000LL ;
115+ return ts;
116+ }
117+
63118/* pybind11 can not deal with (void **) as function input parameters,
64- * therefore we have to cast a simple (void *) pointer to the corresponding type
119+ * therefore cast a simple (void *) pointer to the corresponding type
65120 *
66121 * wrapper bindings, sorted alphabetically
67122 * @param villas_node Name of the module to be bound
@@ -105,10 +160,6 @@ PYBIND11_MODULE(python_binding, m) {
105160 [](void *n) -> const char * { return node_name_short ((vnode *)n); },
106161 py::return_value_policy::copy);
107162
108- m.def (" node_netem_fds" , [](void *n, int fds[]) -> int {
109- return node_netem_fds ((vnode *)n, fds);
110- });
111-
112163 m.def (
113164 " node_new" ,
114165 [](const char *json_str, const char *id_str) -> vnode * {
@@ -132,22 +183,18 @@ PYBIND11_MODULE(python_binding, m) {
132183 return ret;
133184 }
134185 },
135- py::return_value_policy::reference );
186+ py::return_value_policy::take_ownership );
136187
137188 m.def (" node_output_signals_max_cnt" , [](void *n) -> unsigned {
138189 return node_output_signals_max_cnt ((vnode *)n);
139190 });
140191
141192 m.def (" node_pause" , [](void *n) -> int { return node_pause ((vnode *)n); });
142193
143- m.def (" node_poll_fds" , [](void *n, int fds[]) -> int {
144- return node_poll_fds ((vnode *)n, fds);
145- });
146-
147194 m.def (" node_prepare" ,
148195 [](void *n) -> int { return node_prepare ((vsample *)n); });
149196
150- m.def (" node_read" , [](void *n, Array &a, unsigned cnt) -> int {
197+ m.def (" node_read" , [](void *n, SamplesArray &a, unsigned cnt) -> int {
151198 return node_read ((vnode *)n, a.get_smps (), cnt);
152199 });
153200
@@ -178,7 +225,7 @@ PYBIND11_MODULE(python_binding, m) {
178225 return py_str;
179226 });
180227
181- m.def (" node_write" , [](void *n, Array &a, unsigned cnt) -> int {
228+ m.def (" node_write" , [](void *n, SamplesArray &a, unsigned cnt) -> int {
182229 return node_write ((vnode *)n, a.get_smps (), cnt);
183230 });
184231
@@ -187,7 +234,8 @@ PYBIND11_MODULE(python_binding, m) {
187234 });
188235
189236 m.def (
190- " smps_array" , [](unsigned int len) -> Array * { return new Array (len); },
237+ " smps_array" ,
238+ [](unsigned int len) -> SamplesArray * { return new SamplesArray (len); },
191239 py::return_value_policy::take_ownership);
192240
193241 m.def (" sample_alloc" ,
@@ -199,36 +247,128 @@ PYBIND11_MODULE(python_binding, m) {
199247 sample_decref (*smp);
200248 });
201249
202- m.def (" sample_length" ,
203- [](void *smp) -> unsigned { return sample_length ((vsample *)smp); });
250+ m.def (" sample_length" , [](void *smp) -> unsigned {
251+ if (smp) {
252+ return sample_length ((vsample *)smp);
253+ } else {
254+ return -1 ;
255+ }
256+ });
204257
205- m.def (" sample_pack" , &sample_pack, py::return_value_policy::reference);
258+ m.def (
259+ " sample_pack" ,
260+ [](void *s, std::optional<int64_t > ts_origin_ns,
261+ std::optional<int64_t > ts_received_ns) -> vsample * {
262+ struct timespec ts_origin =
263+ ts_origin_ns ? ns_to_timespec (*ts_origin_ns) : time_now ();
264+ struct timespec ts_received =
265+ ts_received_ns ? ns_to_timespec (*ts_received_ns) : time_now ();
266+
267+ auto smp = (villas::node::Sample *)s;
268+ uint64_t *seq = &smp->sequence ;
269+ unsigned len = smp->length ;
270+ double *values = (double *)smp->data ;
271+
272+ return sample_pack (seq, &ts_origin, &ts_received, len, values);
273+ },
274+ py::return_value_policy::reference);
275+
276+ m.def (
277+ " sample_pack" ,
278+ [](const py::list values, std::optional<int64_t > ts_origin_ns,
279+ std::optional<int64_t > ts_received_ns, unsigned seq = 0 ) -> void * {
280+ struct timespec ts_origin =
281+ ts_origin_ns ? ns_to_timespec (*ts_origin_ns) : time_now ();
282+ struct timespec ts_received =
283+ ts_received_ns ? ns_to_timespec (*ts_received_ns) : time_now ();
284+
285+ unsigned values_len = values.size ();
286+ double cvalues[values.size ()];
287+ for (unsigned int i = 0 ; i < values_len; ++i) {
288+ cvalues[i] = values[i].cast <double >();
289+ }
290+ uint64_t sequence = seq;
291+
292+ auto tmp = (void *)sample_pack (&sequence, &ts_origin, &ts_received,
293+ values_len, cvalues);
294+ return tmp;
295+ },
296+ py::return_value_policy::reference);
206297
207298 m.def (
208299 " sample_unpack" ,
209- [](void *smp, unsigned *seq, struct timespec *ts_origin,
210- struct timespec *ts_received, int *flags, unsigned *len,
211- double *values) -> void {
212- return sample_unpack ((vsample *)smp, seq, ts_origin, ts_received, flags,
213- len, values);
300+ [](void *ss, void *ds, std::optional<int64_t > ts_origin_ns,
301+ std::optional<int64_t > ts_received_ns) -> void {
302+ struct timespec ts_origin =
303+ ts_origin_ns ? ns_to_timespec (*ts_origin_ns) : time_now ();
304+ struct timespec ts_received =
305+ ts_received_ns ? ns_to_timespec (*ts_received_ns) : time_now ();
306+
307+ auto srcSmp = (villas::node::Sample **)ss;
308+ auto destSmp = (villas::node::Sample **)ds;
309+
310+ if (!*srcSmp) {
311+ throw std::runtime_error (" Tried to unpack empty sample!" );
312+ }
313+ if (!*destSmp) {
314+ *destSmp = (villas::node::Sample *)sample_alloc ((*srcSmp)->length );
315+ } else if ((*destSmp)->capacity < (*srcSmp)->length ) {
316+ sample_decref (*(vsample **)destSmp);
317+ *destSmp = (villas::node::Sample *)sample_alloc ((*srcSmp)->length );
318+ }
319+
320+ uint64_t *seq = &(*destSmp)->sequence ;
321+ int *flags = &(*destSmp)->flags ;
322+ unsigned *len = &(*destSmp)->length ;
323+ double *values = (double *)(*destSmp)->data ;
324+
325+ sample_unpack (*(vsample **)srcSmp, seq, &ts_origin, &ts_received, flags,
326+ len, values);
214327 },
215328 py::return_value_policy::reference);
216329
217- py::class_<Array>(m, " SamplesArray" )
330+ m.def (" sample_details" , [](void *s) {
331+ auto smp = (villas::node::Sample *)s;
332+ if (!smp) {
333+ return py::dict ();
334+ }
335+
336+ py::dict d;
337+ d[" sequence" ] = smp->sequence ;
338+ d[" length" ] = smp->length ;
339+ d[" capacity" ] = smp->capacity ;
340+ d[" flags" ] = smp->flags ;
341+ d[" refcnt" ] = smp->refcnt .load ();
342+ d[" ts_origin" ] = time_to_double (&smp->ts .origin );
343+ d[" ts_received" ] = time_to_double (&smp->ts .received );
344+
345+ py::list data;
346+ for (unsigned int i = 0 ; i < smp->length ; ++i) {
347+ data.append ((double )smp->data [i]);
348+ }
349+ d[" data" ] = data;
350+
351+ return d;
352+ });
353+
354+ py::class_<SamplesArray>(m, " SamplesArray" )
218355 .def (py::init<unsigned int >(), py::arg (" len" ))
219356 .def (" __getitem__" ,
220- [](Array &a, unsigned int idx) {
357+ [](SamplesArray &a, unsigned int idx) {
221358 assert (idx < a.size () && " Index out of bounds" );
222359 return a[idx];
223360 })
224361 .def (" __setitem__" ,
225- [](Array &a, unsigned int idx, void *smp) {
362+ [](SamplesArray &a, unsigned int idx, void *smp) {
226363 assert (idx < a.size () && " Index out of bounds" );
227364 if (a[idx]) {
228365 sample_decref (a[idx]);
229366 }
230367 a[idx] = (vsample *)smp;
231368 })
232- .def (" get_block" , &Array::get_block)
233- .def (" bulk_alloc" , &Array::bulk_alloc);
369+ .def (" __len__" , &SamplesArray::size)
370+ .def (" bulk_alloc" , &SamplesArray::bulk_alloc)
371+ .def (" grow" , &SamplesArray::grow)
372+ .def (" get_block" , &SamplesArray::get_block)
373+ .def (" clear" , &SamplesArray::clear);
234374}
0 commit comments