]> sigrok.org Git - libsigrok.git/blame - bindings/python/sigrok/core/classes.py
Rename various *PROBE* macros to *CHANNEL*.
[libsigrok.git] / bindings / python / sigrok / core / classes.py
CommitLineData
d8f6e041 1##
50985c20 2## This file is part of the libsigrok project.
d8f6e041
ML
3##
4## Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
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 3 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
20from functools import partial
21from fractions import Fraction
86573018 22from collections import OrderedDict
1cad2115
ML
23from .lowlevel import *
24from . import lowlevel
d8f6e041
ML
25import itertools
26
9bbd6a6a 27__all__ = ['Error', 'Context', 'Driver', 'Device', 'Session', 'Packet', 'Log',
417e9f3a 28 'LogLevel', 'PacketType', 'Quantity', 'Unit', 'QuantityFlag', 'ConfigKey',
3f239f08 29 'ChannelType', 'Channel', 'ChannelGroup', 'InputFormat', 'OutputFormat',
409d85b3 30 'InputFile', 'Output']
d8f6e041
ML
31
32class Error(Exception):
33
34 def __str__(self):
35 return sr_strerror(self.args[0])
36
37def check(result):
38 if result != SR_OK:
39 raise Error(result)
40
d8f6e041
ML
41def gvariant_to_python(value):
42 type_string = g_variant_get_type_string(value)
43 if type_string == 't':
44 return g_variant_get_uint64(value)
45 if type_string == 'b':
46 return g_variant_get_bool(value)
47 if type_string == 'd':
48 return g_variant_get_double(value)
49 if type_string == 's':
50 return g_variant_get_string(value, None)
51 if type_string == '(tt)':
52 return Fraction(
53 g_variant_get_uint64(g_variant_get_child_value(value, 0)),
54 g_variant_get_uint64(g_variant_get_child_value(value, 1)))
1cad2115 55 raise NotImplementedError(
d8f6e041
ML
56 "Can't convert GVariant type '%s' to a Python type." % type_string)
57
58def python_to_gvariant(value):
59 if isinstance(value, int):
60 return g_variant_new_uint64(value)
61 if isinstance(value, bool):
62 return g_variant_new_boolean(value)
63 if isinstance(value, float):
64 return g_variant_new_double(value)
65 if isinstance(value, str):
66 return g_variant_new_string(value)
67 if isinstance(value, Fraction):
68 array = new_gvariant_ptr_array(2)
1e2bd8af
ML
69 gvariant_ptr_array_setitem(array, 0,
70 g_variant_new_uint64(value.numerator))
71 gvariant_ptr_array_setitem(array, 1,
72 g_variant_new_uint64(value.denominator))
d8f6e041
ML
73 result = g_variant_new_tuple(array, 2)
74 delete_gvariant_ptr_array(array)
75 return result
1cad2115 76 raise NotImplementedError(
d8f6e041
ML
77 "Can't convert Python '%s' to a GVariant." % type(value))
78
79def callback_wrapper(session, callback, device_ptr, packet_ptr):
80 device = session.context._devices[int(device_ptr.this)]
81 packet = Packet(session, packet_ptr)
82 callback(device, packet)
83
84class Context(object):
85
86 def __init__(self):
87 context_ptr_ptr = new_sr_context_ptr_ptr()
88 check(sr_init(context_ptr_ptr))
89 self.struct = sr_context_ptr_ptr_value(context_ptr_ptr)
90 self._drivers = None
91 self._devices = {}
a64198c8
ML
92 self._input_formats = None
93 self._output_formats = None
d8f6e041
ML
94 self.session = None
95
96 def __del__(self):
97 sr_exit(self.struct)
98
99 @property
100 def drivers(self):
101 if not self._drivers:
102 self._drivers = {}
103 driver_list = sr_driver_list()
104 for i in itertools.count():
105 driver_ptr = sr_dev_driver_ptr_array_getitem(driver_list, i)
106 if driver_ptr:
107 self._drivers[driver_ptr.name] = Driver(self, driver_ptr)
108 else:
109 break
110 return self._drivers
111
a64198c8
ML
112 @property
113 def input_formats(self):
114 if not self._input_formats:
86573018 115 self._input_formats = OrderedDict()
a64198c8
ML
116 input_list = sr_input_list()
117 for i in itertools.count():
118 input_ptr = sr_input_format_ptr_array_getitem(input_list, i)
119 if input_ptr:
120 self._input_formats[input_ptr.id] = InputFormat(self, input_ptr)
121 else:
122 break
123 return self._input_formats
124
125 @property
126 def output_formats(self):
127 if not self._output_formats:
128 self._output_formats = {}
129 output_list = sr_output_list()
130 for i in itertools.count():
131 output_ptr = sr_output_format_ptr_array_getitem(output_list, i)
132 if output_ptr:
133 self._output_formats[output_ptr.id] = OutputFormat(self, output_ptr)
134 else:
135 break
136 return self._output_formats
137
d8f6e041
ML
138class Driver(object):
139
140 def __init__(self, context, struct):
141 self.context = context
142 self.struct = struct
143 self._initialized = False
144
145 @property
146 def name(self):
147 return self.struct.name
148
3124e80b 149 def scan(self, **kwargs):
d8f6e041
ML
150 if not self._initialized:
151 check(sr_driver_init(self.context.struct, self.struct))
152 self._initialized = True
3124e80b
ML
153 options = []
154 for name, value in kwargs.items():
01e9ff61 155 key = getattr(ConfigKey, name)
3124e80b
ML
156 src = sr_config()
157 src.key = key.id
57dd5e63 158 src.data = python_to_gvariant(value)
3124e80b
ML
159 options.append(src.this)
160 option_list = python_to_gslist(options)
161 device_list = sr_driver_scan(self.struct, option_list)
162 g_slist_free(option_list)
772b21d4 163 devices = [HardwareDevice(self, gpointer_to_sr_dev_inst_ptr(ptr))
05cfe114 164 for ptr in gslist_to_python(device_list)]
d8f6e041
ML
165 g_slist_free(device_list)
166 return devices
167
168class Device(object):
169
772b21d4 170 def __new__(cls, struct, context):
d8f6e041 171 address = int(struct.this)
772b21d4
ML
172 if address not in context._devices:
173 device = super(Device, cls).__new__(cls)
174 device.struct = struct
175 device.context = context
3f239f08 176 device._channels = None
660e398f 177 device._channel_groups = None
772b21d4
ML
178 context._devices[address] = device
179 return context._devices[address]
d8f6e041
ML
180
181 @property
182 def vendor(self):
183 return self.struct.vendor
184
185 @property
186 def model(self):
187 return self.struct.model
188
189 @property
190 def version(self):
191 return self.struct.version
192
417e9f3a 193 @property
3f239f08
UH
194 def channels(self):
195 if self._channels is None:
196 self._channels = {}
197 channel_list = self.struct.channels
198 while (channel_list):
199 channel_ptr = void_ptr_to_sr_channel_ptr(channel_list.data)
200 self._channels[channel_ptr.name] = Channel(self, channel_ptr)
201 channel_list = channel_list.next
202 return self._channels
417e9f3a
ML
203
204 @property
660e398f
UH
205 def channel_groups(self):
206 if self._channel_groups is None:
207 self._channel_groups = {}
208 channel_group_list = self.struct.channel_groups
209 while (channel_group_list):
210 channel_group_ptr = void_ptr_to_sr_channel_group_ptr(
211 channel_group_list.data)
212 self._channel_groups[channel_group_ptr.name] = ChannelGroup(self,
213 channel_group_ptr)
214 channel_group_list = channel_group_list.next
215 return self._channel_groups
417e9f3a 216
772b21d4
ML
217class HardwareDevice(Device):
218
219 def __new__(cls, driver, struct):
220 device = Device.__new__(cls, struct, driver.context)
221 device.driver = driver
222 return device
223
224 def __getattr__(self, name):
225 key = getattr(ConfigKey, name)
226 data = new_gvariant_ptr_ptr()
227 try:
228 check(sr_config_get(self.driver.struct, self.struct, None,
229 key.id, data))
230 except Error as error:
231 if error.errno == SR_ERR_NA:
232 raise NotImplementedError(
233 "Device does not implement %s" % name)
234 else:
235 raise AttributeError
236 value = gvariant_ptr_ptr_value(data)
237 return gvariant_to_python(value)
238
239 def __setattr__(self, name, value):
240 try:
241 key = getattr(ConfigKey, name)
242 except AttributeError:
243 super(Device, self).__setattr__(name, value)
244 return
245 check(sr_config_set(self.struct, None, key.id, python_to_gvariant(value)))
246
3f239f08 247class Channel(object):
417e9f3a
ML
248
249 def __init__(self, device, struct):
250 self.device = device
251 self.struct = struct
252
253 @property
254 def type(self):
3f239f08 255 return ChannelType(self.struct.type)
417e9f3a
ML
256
257 @property
258 def enabled(self):
259 return self.struct.enabled
260
261 @property
262 def name(self):
263 return self.struct.name
264
660e398f 265class ChannelGroup(object):
417e9f3a
ML
266
267 def __init__(self, device, struct):
268 self.device = device
269 self.struct = struct
660e398f 270 self._channels = None
417e9f3a
ML
271
272 def __iter__(self):
660e398f 273 return iter(self.channels)
417e9f3a 274
af54bac9
ML
275 def __getattr__(self, name):
276 key = config_key(name)
277 data = new_gvariant_ptr_ptr()
278 try:
279 check(sr_config_get(self.device.driver.struct, self.device.struct,
945e23a5 280 self.struct, key.id, data))
af54bac9
ML
281 except Error as error:
282 if error.errno == SR_ERR_NA:
283 raise NotImplementedError(
660e398f 284 "Channel group does not implement %s" % name)
af54bac9
ML
285 else:
286 raise AttributeError
287 value = gvariant_ptr_ptr_value(data)
288 return gvariant_to_python(value)
289
290 def __setattr__(self, name, value):
291 try:
292 key = config_key(name)
293 except AttributeError:
660e398f 294 super(ChannelGroup, self).__setattr__(name, value)
af54bac9
ML
295 return
296 check(sr_config_set(self.device.struct, self.struct,
945e23a5 297 key.id, python_to_gvariant(value)))
af54bac9 298
417e9f3a
ML
299 @property
300 def name(self):
301 return self.struct.name
302
303 @property
660e398f
UH
304 def channels(self):
305 if self._channels is None:
306 self._channels = []
307 channel_list = self.struct.channels
308 while (channel_list):
91aea754 309 channel_ptr = void_ptr_to_sr_channel_ptr(channel_list.data)
3f239f08 310 self._channels.append(Channel(self, channel_ptr))
660e398f
UH
311 channel_list = channel_list.next
312 return self._channels
417e9f3a 313
d8f6e041
ML
314class Session(object):
315
316 def __init__(self, context):
317 assert context.session is None
318 self.context = context
319 self.struct = sr_session_new()
320 context.session = self
321
322 def __del__(self):
323 check(sr_session_destroy())
324
325 def add_device(self, device):
326 check(sr_session_dev_add(device.struct))
327
0e77b7ca
UH
328 def open_device(self, device):
329 check(sr_dev_open(device.struct))
330
d8f6e041
ML
331 def add_callback(self, callback):
332 wrapper = partial(callback_wrapper, self, callback)
333 check(sr_session_datafeed_python_callback_add(wrapper))
334
335 def start(self):
336 check(sr_session_start())
337
338 def run(self):
339 check(sr_session_run())
340
341 def stop(self):
342 check(sr_session_stop())
343
344class Packet(object):
345
346 def __init__(self, session, struct):
347 self.session = session
348 self.struct = struct
349 self._payload = None
350
351 @property
352 def type(self):
9bbd6a6a 353 return PacketType(self.struct.type)
d8f6e041
ML
354
355 @property
356 def payload(self):
357 if self._payload is None:
358 pointer = self.struct.payload
9bbd6a6a 359 if self.type == PacketType.LOGIC:
d8f6e041
ML
360 self._payload = Logic(self,
361 void_ptr_to_sr_datafeed_logic_ptr(pointer))
9bbd6a6a 362 elif self.type == PacketType.ANALOG:
15574a3c
ML
363 self._payload = Analog(self,
364 void_ptr_to_sr_datafeed_analog_ptr(pointer))
d8f6e041 365 else:
1cad2115 366 raise NotImplementedError(
417e9f3a 367 "No Python mapping for packet type %s" % self.struct.type)
d8f6e041
ML
368 return self._payload
369
370class Logic(object):
371
372 def __init__(self, packet, struct):
373 self.packet = packet
374 self.struct = struct
375 self._data = None
376
377 @property
378 def data(self):
379 if self._data is None:
380 self._data = cdata(self.struct.data, self.struct.length)
381 return self._data
382
15574a3c
ML
383class Analog(object):
384
385 def __init__(self, packet, struct):
386 self.packet = packet
387 self.struct = struct
388 self._data = None
389
390 @property
391 def num_samples(self):
392 return self.struct.num_samples
393
c2ec42ce
UH
394 @property
395 def mq(self):
9bbd6a6a 396 return Quantity(self.struct.mq)
c2ec42ce
UH
397
398 @property
399 def unit(self):
9bbd6a6a 400 return Unit(self.struct.unit)
c2ec42ce
UH
401
402 @property
403 def mqflags(self):
9bbd6a6a 404 return QuantityFlag.set_from_mask(self.struct.mqflags)
c2ec42ce 405
15574a3c
ML
406 @property
407 def data(self):
408 if self._data is None:
409 self._data = float_array.frompointer(self.struct.data)
410 return self._data
411
816aed6c
UH
412class Log(object):
413
816aed6c
UH
414 @property
415 def level(self):
9bbd6a6a 416 return LogLevel(sr_log_loglevel_get())
816aed6c
UH
417
418 @level.setter
419 def level(self, l):
9bbd6a6a 420 check(sr_log_loglevel_set(l.id))
816aed6c
UH
421
422 @property
423 def domain(self):
424 return sr_log_logdomain_get()
425
426 @domain.setter
427 def domain(self, d):
428 check(sr_log_logdomain_set(d))
429
a64198c8
ML
430class InputFormat(object):
431
432 def __init__(self, context, struct):
433 self.context = context
434 self.struct = struct
435
436 @property
437 def id(self):
438 return self.struct.id
439
440 @property
441 def description(self):
442 return self.struct.description
443
f0e764de
ML
444 def format_match(self, filename):
445 return bool(self.struct.call_format_match(filename))
446
447class InputFile(object):
448
449 def __init__(self, format, filename, **kwargs):
450 self.format = format
451 self.filename = filename
452 self.struct = sr_input()
453 self.struct.format = self.format.struct
454 self.struct.param = g_hash_table_new_full(
409d85b3 455 g_str_hash_ptr, g_str_equal_ptr, g_free_ptr, g_free_ptr)
f0e764de
ML
456 for key, value in kwargs.items():
457 g_hash_table_insert(self.struct.param, g_strdup(key), g_strdup(str(value)))
458 check(self.format.struct.call_init(self.struct, self.filename))
772b21d4 459 self.device = InputFileDevice(self)
f0e764de
ML
460
461 def load(self):
462 check(self.format.struct.call_loadfile(self.struct, self.filename))
463
464 def __del__(self):
465 g_hash_table_destroy(self.struct.param)
466
772b21d4
ML
467class InputFileDevice(Device):
468
469 def __new__(cls, file):
470 device = Device.__new__(cls, file.struct.sdi, file.format.context)
471 device.file = file
472 return device
473
a64198c8
ML
474class OutputFormat(object):
475
476 def __init__(self, context, struct):
477 self.context = context
478 self.struct = struct
479
480 @property
481 def id(self):
482 return self.struct.id
483
484 @property
485 def description(self):
486 return self.struct.description
487
409d85b3
ML
488class Output(object):
489
490 def __init__(self, format, device, param=None):
491 self.format = format
492 self.device = device
493 self.param = param
494 self.struct = sr_output()
495 self.struct.format = self.format.struct
496 self.struct.sdi = self.device.struct
497 self.struct.param = param
498 check(self.format.struct.call_init(self.struct))
499
500 def receive(self, packet):
501
502 output_buf_ptr = new_uint8_ptr_ptr()
503 output_len_ptr = new_uint64_ptr()
504 using_obsolete_api = False
505
506 if self.format.struct.event and packet.type in (
507 PacketType.TRIGGER, PacketType.FRAME_BEGIN,
508 PacketType.FRAME_END, PacketType.END):
509 check(self.format.struct.call_event(self.struct, packet.type.id,
510 output_buf_ptr, output_len_ptr))
511 using_obsolete_api = True
512 elif self.format.struct.data and packet.type.id == self.format.struct.df_type:
513 check(self.format.struct.call_data(self.struct,
514 packet.payload.struct.data, packet.payload.struct.length,
515 output_buf_ptr, output_len_ptr))
516 using_obsolete_api = True
517
518 if using_obsolete_api:
519 output_buf = uint8_ptr_ptr_value(output_buf_ptr)
520 output_len = uint64_ptr_value(output_len_ptr)
521 result = cdata(output_buf, output_len)
522 g_free(output_buf)
523 return result
524
525 if self.format.struct.receive:
526 out_ptr = new_gstring_ptr_ptr()
527 check(self.format.struct.call_receive(self.struct, self.device.struct,
528 packet.struct, out_ptr))
529 out = gstring_ptr_ptr_value(out_ptr)
530 if out:
531 result = out.str
532 g_string_free(out, True)
533 return result
534
535 return None
536
537 def __del__(self):
538 check(self.format.struct.call_cleanup(self.struct))
539
8593c8e3
ML
540class ConfigInfo(object):
541
14e8eb33
ML
542 def __new__(cls, key):
543 struct = sr_config_info_get(key.id)
544 if not struct:
545 return None
546 obj = super(ConfigInfo, cls).__new__(cls)
547 obj.key = key
548 obj.struct = struct
549 return obj
8593c8e3
ML
550
551 @property
552 def datatype(self):
553 return DataType(self.struct.datatype)
554
555 @property
556 def id(self):
557 return self.struct.id
558
559 @property
560 def name(self):
561 return self.struct.name
562
563 @property
564 def description(self):
565 return self.struct.description
566
9bbd6a6a
ML
567class EnumValue(object):
568
569 _enum_values = {}
570
571 def __new__(cls, id):
572 if cls not in cls._enum_values:
573 cls._enum_values[cls] = {}
574 if id not in cls._enum_values[cls]:
575 value = super(EnumValue, cls).__new__(cls)
576 value.id = id
577 cls._enum_values[cls][id] = value
578 return cls._enum_values[cls][id]
579
580class LogLevel(EnumValue):
581 pass
582
583class PacketType(EnumValue):
584 pass
585
586class Quantity(EnumValue):
587 pass
588
589class Unit(EnumValue):
590 pass
591
592class QuantityFlag(EnumValue):
593
594 @classmethod
595 def set_from_mask(cls, mask):
596 result = set()
597 while mask:
598 new_mask = mask & (mask - 1)
599 result.add(cls(mask ^ new_mask))
600 mask = new_mask
601 return result
602
f245b766
ML
603class ConfigKey(EnumValue):
604 pass
605
8593c8e3
ML
606class DataType(EnumValue):
607 pass
608
3f239f08 609class ChannelType(EnumValue):
417e9f3a
ML
610 pass
611
d8f6e041 612for symbol_name in dir(lowlevel):
9bbd6a6a
ML
613 for prefix, cls in [
614 ('SR_LOG_', LogLevel),
615 ('SR_DF_', PacketType),
616 ('SR_MQ_', Quantity),
617 ('SR_UNIT_', Unit),
f245b766 618 ('SR_MQFLAG_', QuantityFlag),
417e9f3a 619 ('SR_CONF_', ConfigKey),
8593c8e3 620 ('SR_T_', DataType),
3f239f08 621 ('SR_CHANNEL_', ChannelType)]:
9bbd6a6a
ML
622 if symbol_name.startswith(prefix):
623 name = symbol_name[len(prefix):]
624 value = getattr(lowlevel, symbol_name)
cad0acef
ML
625 obj = cls(value)
626 setattr(cls, name, obj)
627 if cls is ConfigKey:
628 obj.info = ConfigInfo(obj)
629 if obj.info:
630 setattr(cls, obj.info.id, obj)
01e9ff61
ML
631 else:
632 setattr(cls, name.lower(), obj)