]> sigrok.org Git - libsigrok.git/blob - bindings/ruby/classes.i
korad-kaxxxxp: use ID text prefix with optional version for RND models
[libsigrok.git] / bindings / ruby / classes.i
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2016 Aurelien Jacobs <aurel@gnuage.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 %define DOCSTRING
21 "
22 = Introduction
23
24 The sigrok Ruby API provides an object-oriented Ruby interface to the
25 functionality in libsigrok. It is built on top of the libsigrokcxx C++ API.
26
27 = Getting started
28
29 Usage of the sigrok Ruby API needs to begin with a call to Context.create().
30 This will create the global libsigrok context and returns a Context object.
31 Methods on this object provide access to the hardware drivers, input and output
32 formats supported by the library, as well as means of creating other objects
33 such as sessions and triggers.
34
35 = Error handling
36
37 When any libsigrok C API call returns an error, an Error exception is raised,
38 which provides access to the error code and description."
39 %enddef
40
41 %module(docstring=DOCSTRING) sigrok
42
43 %{
44 #include "config.h"
45
46 #include <stdio.h>
47 #include <glibmm.h>
48 %}
49
50 %include "../swig/templates.i"
51
52 %{
53 static const char *string_from_ruby(VALUE obj)
54 {
55     switch (TYPE(obj)) {
56     case T_STRING:
57         return StringValueCStr(obj);
58     case T_SYMBOL:
59         return rb_id2name(SYM2ID(obj));
60     default:
61         throw sigrok::Error(SR_ERR_ARG);
62     }
63 }
64
65 /* Convert from Glib::Variant to native Ruby types. */
66 static VALUE variant_to_ruby(Glib::VariantBase variant)
67 {
68     if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL)) {
69         return Glib::VariantBase::cast_dynamic< Glib::Variant<bool> >(variant).get() ? Qtrue : Qfalse;
70     } else if (variant.is_of_type(Glib::VARIANT_TYPE_BYTE)) {
71         return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<unsigned char> >(variant).get());
72     } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT16)) {
73         return INT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint16> >(variant).get());
74     } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT16)) {
75         return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint16> >(variant).get());
76     } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32)) {
77         return INT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint32> >(variant).get());
78     } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) {
79         return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint32> >(variant).get());
80     } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT64)) {
81         return LL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint64> >(variant).get());
82     } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64)) {
83         return ULL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint64> >(variant).get());
84     } else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE)) {
85         return DBL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<double> >(variant).get());
86     } else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING)) {
87         auto str = Glib::VariantBase::cast_dynamic< Glib::Variant<std::string> >(variant).get();
88         return rb_str_new(str.c_str(), str.length());
89     } else if (variant.is_of_type(Glib::VARIANT_TYPE_VARIANT)) {
90         auto var = Glib::VariantBase::cast_dynamic< Glib::Variant<Glib::VariantBase> >(variant).get();
91         return variant_to_ruby(var);
92     } else if (variant.is_container()) {
93         Glib::VariantContainerBase container = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase > (variant);
94         gsize count = container.get_n_children();
95         if (container.is_of_type(Glib::VARIANT_TYPE_DICTIONARY)) {
96             VALUE hash = rb_hash_new();
97             for (gsize i = 0; i < count; i++) {
98                 Glib::VariantContainerBase entry = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase > (container.get_child(i));
99                 VALUE key = variant_to_ruby(entry.get_child(0));
100                 VALUE val = variant_to_ruby(entry.get_child(1));
101                 rb_hash_aset(hash, key, val);
102             }
103             return hash;
104         } else if (container.is_of_type(Glib::VARIANT_TYPE_ARRAY) ||
105                    container.is_of_type(Glib::VARIANT_TYPE_TUPLE)) {
106             VALUE array = rb_ary_new2(count);
107             for (gsize i = 0; i < count; i++) {
108                 VALUE val = variant_to_ruby(container.get_child(i));
109                 rb_ary_store(array, i, val);
110             }
111             return array;
112         }
113     } else {
114         SWIG_exception(SWIG_TypeError, ("TODO: GVariant(" + variant.get_type().get_string() + ") -> Ruby").c_str());
115     }
116     return 0; /* Dummy, to avoid a compiler warning. */
117 }
118 %}
119
120 /* Map from Glib::Variant to native Ruby types. */
121 %typemap(out) Glib::VariantBase {
122     $result = variant_to_ruby($1);
123 }
124
125 /* Map from Glib::VariantContainer to native Ruby types. */
126 %typemap(out) Glib::VariantContainerBase {
127     $result = variant_to_ruby($1);
128 }
129
130 /* Map from callable Ruby Proc to LogCallbackFunction */
131 %typemap(in) sigrok::LogCallbackFunction {
132     if (!rb_obj_is_proc($input))
133         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
134
135     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
136     rb_gc_register_address(proc.get());
137
138     $1 = [=] (const sigrok::LogLevel *loglevel, std::string message) {
139         VALUE log_obj = SWIG_NewPointerObj(
140                 SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0);
141
142         VALUE string_obj = rb_external_str_new_with_enc(message.c_str(), message.length(), rb_utf8_encoding());
143
144         VALUE args = rb_ary_new3(2, log_obj, string_obj);
145         rb_proc_call(*proc.get(), args);
146     };
147 }
148
149 /* Map from callable Ruby Proc to SessionStoppedCallback */
150 %typemap(in) sigrok::SessionStoppedCallback {
151     if (!rb_obj_is_proc($input))
152         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
153
154     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
155     rb_gc_register_address(proc.get());
156
157     $1 = [=] () {
158         rb_proc_call(*proc.get(), rb_ary_new());
159     };
160 }
161
162 /* Map from callable Ruby Proc to DatafeedCallbackFunction */
163 %typemap(in) sigrok::DatafeedCallbackFunction {
164     if (!rb_obj_is_proc($input))
165         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
166
167     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
168     rb_gc_register_address(proc.get());
169
170     $1 = [=] (std::shared_ptr<sigrok::Device> device,
171             std::shared_ptr<sigrok::Packet> packet) {
172         VALUE device_obj = SWIG_NewPointerObj(
173             SWIG_as_voidptr(new std::shared_ptr<sigrok::Device>(device)),
174             SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN);
175
176         VALUE packet_obj = SWIG_NewPointerObj(
177             SWIG_as_voidptr(new std::shared_ptr<sigrok::Packet>(packet)),
178             SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN);
179
180         VALUE args = rb_ary_new3(2, device_obj, packet_obj);
181         rb_proc_call(*proc.get(), args);
182     };
183 }
184
185 /* Cast PacketPayload pointers to correct subclass type. */
186 %ignore sigrok::Packet::payload;
187 %rename sigrok::Packet::_payload payload;
188 %extend sigrok::Packet
189 {
190     VALUE _payload()
191     {
192         if ($self->type() == sigrok::PacketType::HEADER) {
193             return SWIG_NewPointerObj(
194                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Header>(dynamic_pointer_cast<sigrok::Header>($self->payload()))),
195                 SWIGTYPE_p_std__shared_ptrT_sigrok__Header_t, SWIG_POINTER_OWN);
196         } else if ($self->type() == sigrok::PacketType::META) {
197             return SWIG_NewPointerObj(
198                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Meta>(dynamic_pointer_cast<sigrok::Meta>($self->payload()))),
199                 SWIGTYPE_p_std__shared_ptrT_sigrok__Meta_t, SWIG_POINTER_OWN);
200         } else if ($self->type() == sigrok::PacketType::ANALOG) {
201             return SWIG_NewPointerObj(
202                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Analog>(dynamic_pointer_cast<sigrok::Analog>($self->payload()))),
203                 SWIGTYPE_p_std__shared_ptrT_sigrok__Analog_t, SWIG_POINTER_OWN);
204         } else if ($self->type() == sigrok::PacketType::LOGIC) {
205             return SWIG_NewPointerObj(
206                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Logic>(dynamic_pointer_cast<sigrok::Logic>($self->payload()))),
207                 SWIGTYPE_p_std__shared_ptrT_sigrok__Logic_t, SWIG_POINTER_OWN);
208         } else {
209             return Qnil;
210         }
211     }
212 }
213
214 %{
215
216 #include "libsigrokcxx/libsigrokcxx.hpp"
217
218 /* Convert from a Ruby type to Glib::Variant, according to config key data type. */
219 Glib::VariantBase ruby_to_variant_by_key(VALUE input, const sigrok::ConfigKey *key)
220 {
221     enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
222
223     if (type == SR_T_UINT64 && RB_TYPE_P(input, T_FIXNUM))
224         return Glib::Variant<guint64>::create(NUM2ULL(input));
225     if (type == SR_T_UINT64 && RB_TYPE_P(input, T_BIGNUM))
226         return Glib::Variant<guint64>::create(NUM2ULL(input));
227     else if (type == SR_T_STRING && RB_TYPE_P(input, T_STRING))
228         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
229     else if (type == SR_T_STRING && RB_TYPE_P(input, T_SYMBOL))
230         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
231     else if (type == SR_T_BOOL && RB_TYPE_P(input, T_TRUE))
232         return Glib::Variant<bool>::create(true);
233     else if (type == SR_T_BOOL && RB_TYPE_P(input, T_FALSE))
234         return Glib::Variant<bool>::create(false);
235     else if (type == SR_T_FLOAT && RB_TYPE_P(input, T_FLOAT))
236         return Glib::Variant<double>::create(RFLOAT_VALUE(input));
237     else if (type == SR_T_INT32 && RB_TYPE_P(input, T_FIXNUM))
238         return Glib::Variant<gint32>::create(NUM2INT(input));
239     else
240         throw sigrok::Error(SR_ERR_ARG);
241 }
242
243 /* Convert from a Ruby type to Glib::Variant, according to Option data type. */
244 Glib::VariantBase ruby_to_variant_by_option(VALUE input, std::shared_ptr<sigrok::Option> option)
245 {
246     Glib::VariantBase variant = option->default_value();
247
248     if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_FIXNUM))
249         return Glib::Variant<guint64>::create(NUM2ULL(input));
250     else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_BIGNUM))
251         return Glib::Variant<guint64>::create(NUM2ULL(input));
252     else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_STRING))
253         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
254     else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_SYMBOL))
255         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
256     else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_TRUE))
257         return Glib::Variant<bool>::create(true);
258     else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_FALSE))
259         return Glib::Variant<bool>::create(false);
260     else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE) && RB_TYPE_P(input, T_FLOAT))
261         return Glib::Variant<double>::create(RFLOAT_VALUE(input));
262     else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32) && RB_TYPE_P(input, T_FIXNUM))
263         return Glib::Variant<gint32>::create(NUM2INT(input));
264     else
265         throw sigrok::Error(SR_ERR_ARG);
266 }
267
268 struct hash_to_map_options_params {
269     std::map<std::string, std::shared_ptr<sigrok::Option> > options;
270     std::map<std::string, Glib::VariantBase> output;
271 };
272
273 int convert_option(VALUE key, VALUE val, VALUE in) {
274     struct hash_to_map_options_params *params;
275     params = (struct hash_to_map_options_params *)in;
276
277     auto k = string_from_ruby(key);
278     auto v = ruby_to_variant_by_option(val, params->options[k]);
279     params->output[k] = v;
280
281     return ST_CONTINUE;
282 }
283
284 /* Convert from a Ruby hash to a std::map<std::string, Glib::VariantBase> */
285 std::map<std::string, Glib::VariantBase> hash_to_map_options(VALUE hash,
286     std::map<std::string, std::shared_ptr<sigrok::Option> > options)
287 {
288     if (!RB_TYPE_P(hash, T_HASH))
289         throw sigrok::Error(SR_ERR_ARG);
290
291     struct hash_to_map_options_params params = { options };
292     rb_hash_foreach(hash, (int (*)(ANYARGS))convert_option, (VALUE)&params);
293
294     return params.output;
295 }
296
297 int convert_option_by_key(VALUE key, VALUE val, VALUE in) {
298     std::map<const sigrok::ConfigKey *, Glib::VariantBase> *options;
299     options = (std::map<const sigrok::ConfigKey *, Glib::VariantBase> *)in;
300
301     auto k = sigrok::ConfigKey::get_by_identifier(string_from_ruby(key));
302     auto v = ruby_to_variant_by_key(val, k);
303     (*options)[k] = v;
304
305     return ST_CONTINUE;
306 }
307
308 %}
309
310 /* Ignore these methods, we will override them below. */
311 %ignore sigrok::Analog::data;
312 %ignore sigrok::Driver::scan;
313 %ignore sigrok::Input::send;
314 %ignore sigrok::InputFormat::create_input;
315 %ignore sigrok::OutputFormat::create_output;
316
317 %include "doc.i"
318
319 %define %attributevector(Class, Type, Name, Get)
320 %alias sigrok::Class::_ ## Get #Name;
321 %enddef
322
323 %define %attributemap(Class, Type, Name, Get)
324 %alias sigrok::Class::_ ## Get #Name;
325 %enddef
326
327 %define %enumextras(Class)
328 %extend sigrok::Class
329 {
330     VALUE to_s()
331     {
332         std::string str = $self->name();
333         return rb_external_str_new_with_enc(str.c_str(), str.length(), rb_utf8_encoding());
334     }
335
336     bool operator==(void *other)
337     {
338         return (long) $self == (long) other;
339     }
340 }
341 %enddef
342
343 %include "../swig/classes.i"
344
345 /* Replace the original Driver.scan with a keyword arguments version. */
346 %rename sigrok::Driver::_scan scan;
347 %extend sigrok::Driver
348 {
349     std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan(VALUE kwargs = rb_hash_new())
350     {
351         if (!RB_TYPE_P(kwargs, T_HASH))
352             throw sigrok::Error(SR_ERR_ARG);
353
354         std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
355         rb_hash_foreach(kwargs, (int (*)(ANYARGS))convert_option_by_key, (VALUE)&options);
356
357         return $self->scan(options);
358     }
359 }
360
361 /* Support Input.send() with string argument. */
362 %rename sigrok::Input::_send send;
363 %extend sigrok::Input
364 {
365     void _send(VALUE data)
366     {
367         data = StringValue(data);
368         return $self->send(RSTRING_PTR(data), RSTRING_LEN(data));
369     }
370 }
371
372 /* Support InputFormat.create_input() with keyword arguments. */
373 %rename sigrok::InputFormat::_create_input create_input;
374 %extend sigrok::InputFormat
375 {
376     std::shared_ptr<sigrok::Input> _create_input(VALUE hash = rb_hash_new())
377     {
378         return $self->create_input(hash_to_map_options(hash, $self->options()));
379     }
380 }
381
382 /* Support OutputFormat.create_output() with keyword arguments. */
383 %rename sigrok::OutputFormat::_create_output create_output;
384 %extend sigrok::OutputFormat
385 {
386     std::shared_ptr<sigrok::Output> _create_output(
387         std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
388     {
389         return $self->create_output(device,
390             hash_to_map_options(hash, $self->options()));
391     }
392
393     std::shared_ptr<sigrok::Output> _create_output(string filename,
394         std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
395     {
396         return $self->create_output(filename, device,
397             hash_to_map_options(hash, $self->options()));
398     }
399 }
400
401 /* Support config_set() with Ruby input types. */
402 %extend sigrok::Configurable
403 {
404     void config_set(const ConfigKey *key, VALUE input)
405     {
406         $self->config_set(key, ruby_to_variant_by_key(input, key));
407     }
408 }
409
410 /* Return Ruby array from Analog::data(). */
411 %rename sigrok::Analog::_data data;
412 %extend sigrok::Analog
413 {
414     VALUE _data()
415     {
416         int num_channels = $self->channels().size();
417         int num_samples  = $self->num_samples();
418         float *data = (float *) $self->data_pointer();
419         VALUE channels = rb_ary_new2(num_channels);
420         for(int i = 0; i < num_channels; i++) {
421             VALUE samples = rb_ary_new2(num_samples);
422             for (int j = 0; j < num_samples; j++) {
423                 rb_ary_store(samples, j, DBL2NUM(data[i*num_samples+j]));
424             }
425             rb_ary_store(channels, i, samples);
426         }
427         return channels;
428     }
429 }