Skip to content

Commit e2e732d

Browse files
author
Kevin Vu te Laar
committed
Rework Binding Wrapper
Binding rework to a class design. Node: - Stores SamplesArray, Config, cpp-Node handle. - Automatically deals with SamplesArray sizing and (de-)allocation of samples. - Removed `node_netem_fds()` and `node_poll_fds`. - `sample_pack()` and `sample_unpack` can only read/write samples to a SamplesArray within a node. - Added `sample_details()` for inspecting sample content. - Added `_SamplesArray` helper class to enable more pythonic syntax. Signed-off-by: Kevin Vu te Laar <vu.te@rwth-aachen.de>
1 parent f023f4d commit e2e732d

2 files changed

Lines changed: 605 additions & 316 deletions

File tree

python/binding/capi_python_binding.cpp

Lines changed: 176 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,44 @@
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

1523
extern "C" {
1624
#include <villas/node.h>
1725
}
1826

1927
namespace py = pybind11;
2028

21-
class Array {
29+
class SamplesArray {
2230
public:
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

Comments
 (0)