Ran update-copyright.py.
[pycomedi.git] / pycomedi / constant.pyx
1 # Copyright (C) 2011-2012 W. Trevor King <wking@tremily.us>
2 #
3 # This file is part of pycomedi.
4 #
5 # pycomedi is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 2 of the License, or (at your option) any later
8 # version.
9 #
10 # pycomedi is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # pycomedi.  If not, see <http://www.gnu.org/licenses/>.
16
17 """Enums and flags are bundled into class instances for easier browsing
18
19 >>> SUBDEVICE_TYPE  # doctest: +NORMALIZE_WHITESPACE
20 [<_NamedInt unused>, <_NamedInt ai>, <_NamedInt ao>, <_NamedInt di>,
21  <_NamedInt do>, <_NamedInt dio>, <_NamedInt counter>, <_NamedInt timer>,
22  <_NamedInt memory>, <_NamedInt calib>, <_NamedInt proc>, <_NamedInt serial>,
23  <_NamedInt pwm>]
24 >>> SUBDEVICE_TYPE.dio
25 <_NamedInt dio>
26 >>> SUBDEVICE_TYPE.dio.value == _comedi.COMEDI_SUBD_DIO
27 True
28 >>> SUBDEVICE_TYPE.dio.doc
29 'COMEDI_SUBD_DIO (digital input/output)'
30
31 You can also search by name or value.
32
33 >>> TRIG_SRC.index_by_name('timer')
34 <_NamedInt timer>
35 >>> TRIG_SRC.index_by_value(_comedi.TRIG_NOW)
36 <_NamedInt now>
37
38 Some flags have constants for setting or clearing all the flags at once.
39
40 >>> TRIG_SRC  # doctest: +NORMALIZE_WHITESPACE
41 [<_NamedInt none>, <_NamedInt now>, <_NamedInt follow>, <_NamedInt time>,
42  <_NamedInt timer>, <_NamedInt count>, <_NamedInt ext>, <_NamedInt int>,
43  <_NamedInt other>]
44 >>> TRIG_SRC._empty
45 <_NamedInt invalid>
46 >>> TRIG_SRC._all
47 <_NamedInt any>
48
49 Flag instances have a special wrapper that stores their value.
50
51 >>> f = FlagValue(SDF, 17)
52 >>> f.flag  # doctest: +ELLIPSIS
53 [<_NamedInt busy>, <_NamedInt busy_owner>, ...]
54 >>> f.busy
55 True
56 >>> f.busy = False
57 >>> f._value
58 16
59
60 You can treat named integers as Python integers with bitwise operations,
61
62 >>> a = TRIG_SRC.now | TRIG_SRC.follow | TRIG_SRC.time | 64
63 >>> a
64 <BitwiseOperator 78>
65 >>> a.value
66 78
67 >>> TRIG_SRC.none & TRIG_SRC.now
68 <BitwiseOperator 0>
69
70 Because of the way Python operator overloading works [#ops]_, you can
71 also start a bitwise chain with an integer.
72
73 >>> 64 | TRIG_SRC.now
74 <BitwiseOperator 66>
75
76 Rich comparisons with other flags and integers are also supported.
77
78 >>> 64 > TRIG_SRC.now
79 True
80 >>> TRIG_SRC.now > 64
81 False
82 >>> TRIG_SRC.now >= 2
83 True
84 >>> TRIG_SRC.now >= 3
85 False
86 >>> import copy
87 >>> TRIG_SRC.now == copy.deepcopy(TRIG_SRC.now)
88 True
89 >>> TRIG_SRC.now != TRIG_SRC.now
90 False
91 >>> TRIG_SRC.now <= 2
92 True
93 >>> TRIG_SRC.now < 3
94 True
95 >>> TRIG_SRC.now > None
96 True
97
98 The ``UNIT`` constant treats ``RF_EXTERNAL`` as a full-fledged unit:
99
100 >>> UNIT.index_by_name('external')
101 <_NamedInt external>
102 >>> UNIT.index_by_value(_comedi.RF_EXTERNAL)
103 <_NamedInt external>
104
105 .. [#ops] See `emulating numeric types`_ and `NotImplementedError` in
106    `the standard type hierarchy`_.
107
108 .. _emulating numeric types:
109   http://docs.python.org/reference/datamodel.html#emulating-numeric-types
110 .. _the standard type hierarchy`_
111   http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy
112 """
113
114 from math import log as _log
115 import sys as _sys
116
117 import numpy as _numpy
118 import comedi as _comedi
119
120 from pycomedi import LOG as _LOG
121
122
123 def bitwise_value(object):
124     """Convenience function for flexible value specification
125
126     This funciton makes it easy for functions and methods to accept
127     either integers or `BitwiseOperator` instances as integer
128     parameters.
129     """
130     if isinstance(object, BitwiseOperator):
131         return object.value
132     return object
133
134
135 def _pso(s, o):
136     """External definition of staticmethod until Cython supports the
137     usual syntax.
138
139     http://docs.cython.org/src/userguide/limitations.html#behaviour-of-class-scopes
140     """
141     if isinstance(s, BitwiseOperator):
142         s = s.value
143     if isinstance(o, BitwiseOperator):
144         o = o.value
145     return (long(s), long(o))
146
147
148 cdef class BitwiseOperator (object):
149     """General class for bitwise operations.
150
151     This class allows chaining bitwise operations between
152     `_NamedInt`\s and similar objects.
153
154     Because flag values can be large, we cast all values to longs
155     before performing any bitwise operations.  This avoids issues like
156
157     >>> int.__or__(1073741824, 2147483648L)
158     NotImplemented
159     """
160
161     def __init__(self, value):
162         self.value = value
163
164     def __str__(self):
165         return self.__repr__()
166
167     def __repr__(self):
168         return '<%s %s>' % (self.__class__.__name__, self.value)
169
170     _prepare_self_other = staticmethod(_pso)
171
172     def __and__(self, other):
173         "Bitwise and acts on `BitwiseOperator.value`."
174         s,o = BitwiseOperator._prepare_self_other(self, other)
175         return BitwiseOperator(int(long.__and__(s, o)))
176
177     def __or__(self, other):
178         "Bitwise or acts on `BitwiseOperator.value`."
179         s,o = BitwiseOperator._prepare_self_other(self, other)
180         return BitwiseOperator(int(long.__or__(s, o)))
181
182     def __richcmp__(self, other, op):
183         if other is None:
184             other = self.value - 1
185         s,o = BitwiseOperator._prepare_self_other(self, other)
186         if op == 0:
187             return s < o
188         elif op == 1:
189             return s <= o
190         elif op == 2:
191             return s == o
192         elif op == 3:
193             return s != o
194         elif op == 4:
195             return s > o
196         elif op == 5:
197             return s >= o
198         else:
199             raise ValueError(op)
200
201     def __reduce__(self):
202         return (BitwiseOperator, (self.value,))
203
204
205 class _NamedInt (BitwiseOperator):
206     "A flag or enum item."
207     def __init__(self, name, value, doc=None):
208         super(_NamedInt, self).__init__(value)
209         self.name = name
210         self.doc = doc
211
212     def __str__(self):
213         return self.name
214
215     def __repr__(self):
216         return '<%s %s>' % (self.__class__.__name__, self.name)
217
218     def __reduce__(self):
219         return (_NamedInt, (self.name, self.value, self.doc))
220
221
222 class _Enum (list):
223     "An enumerated list"
224     def __init__(self, name, prefix='', blacklist=None, whitelist=None,
225                  translation=None):
226         super(_Enum, self).__init__()
227         self.name = name
228         if blacklist == None:
229             blacklist = []
230         if translation == None:
231             translation = {}
232         self._name_keys = {}
233         self._value_keys = {}
234         for attr in dir(_comedi):
235             if attr.startswith(prefix):
236                 item_name = self._item_name(attr, prefix, translation)
237                 if self._is_ignored(item_name, blacklist, whitelist):
238                     continue
239                 self._add_item(attr, item_name)
240         self.sort(key=lambda item: item.value)
241
242     def _item_name(self, attr, prefix, translation):
243         item_name = attr[len(prefix):]
244         if item_name in translation:
245             return translation[item_name]
246         else:
247             return item_name.lower()
248
249     def _is_ignored(self, item_name, blacklist, whitelist):
250         return (item_name in blacklist
251                 or whitelist != None and item_name not in whitelist)
252
253     def _add_item(self, attr, item_name):
254         item_value = getattr(_comedi, attr)
255         if item_value < 0:
256             _LOG.debug('big value for {0:s}: {1:d} ({1:b}) converted to {2:d} ({2:b})'.format(
257                     attr, item_value, (1<<32) + item_value))
258             item_value = (1<<32) + item_value  # flags are unsigned 32 bit integers, but SWIG signs them
259         item = _NamedInt(item_name, item_value, doc=attr)
260         self.append(item)
261
262     def append(self, item):
263         super(_Enum, self).append(item)
264         self._name_keys[item.name] = item
265         if item.value in self._value_keys and item.name:
266             raise ValueError('value collision in %s: %s = %s = %#x'
267                              % (self.name, item.name,
268                                 self._value_keys[item.value], item.value))
269         self._value_keys[item.value] = item
270         setattr(self, item.name, item)
271
272     def index_by_name(self, name):
273         return self._name_keys[name]
274
275     def index_by_value(self, value):
276         return self._value_keys[value]
277
278
279 class _Flag (_Enum):
280     "A flag"
281     def __init__(self, *args, **kwargs):
282         super(_Flag, self).__init__(*args, **kwargs)
283         self._empty = None
284         self._all = None
285         for flag in self:
286             if flag.value == 0:
287                 self._empty = flag
288             elif flag.value < 0 or abs(_log(flag.value, 2)) % 1 > 1e-13:  # deal with rounding errors
289                 if self._all:
290                     raise ValueError(
291                         'mutliple multi-bit flags in %s: %s = %#x and %s = %#x'
292                         % (self.name, self._all.name, self._all.value,
293                            flag.name, flag.value))
294                 self._all = flag
295         if self._empty:
296             self.remove(self._empty)
297         if self._all:
298             self.remove(self._all)
299
300     def get(self, value, name):
301         flag = getattr(self, name)
302         assert flag.value != 0, '%s: %s' % (self.name, flag)
303         return value & flag.value == flag.value
304
305     def set(self, value, name, status):
306         flag = getattr(self, name)
307         if status == True:
308             return value | flag.value
309         return (value | flag.value) - flag.value
310
311
312 class FlagValue (object):
313     """A flag instance (flag + value)
314
315     Examples
316     --------
317
318     >>> f = FlagValue(flag=TRIG_SRC, value=0, default='empty')
319     >>> f.any
320     False
321     >>> print f
322     empty
323     >>> f.now = True
324     >>> f.timer = True
325     >>> f.int = True
326     >>> print f
327     now|timer|int
328     """
329     def __init__(self, flag, value, default='-'):
330         self.flag = flag
331         self._value = value
332         self._default = default
333
334     def __str__(self):
335         flags = [f for f in self.flag if getattr(self, f.name)]
336         if len(flags) == 0:
337             return self._default
338         return '|'.join([f.name for f in flags])
339
340     def __getattr__(self, name):
341         return self.flag.get(self._value, name)
342
343     def __setattr__(self, name, value):
344         if name != 'flag' and not name.startswith('_'):
345             value = self.flag.set(self._value, name, value)
346             name = '_value'
347         super(FlagValue, self).__setattr__(name, value)
348
349
350 # blacklist deprecated values (and those belonging to other _Enums or _Flags)
351
352 CR = _Flag('ChanSpec flags', 'CR_', blacklist=['dither', 'deglitch'])
353 CR.alt_filter.doc += ' (can also mean "dither" or "deglitch")'
354
355 AREF = _Enum('analog_reference', 'AREF_')
356 AREF.diff.doc += ' (differential)'
357 AREF.other.doc += ' (other / undefined)'
358
359 #GPCT = _Flag('general_purpose_counter_timer', 'GPCT_')
360 # Two competing flag sets?  Need some documentation.
361
362 INSN_MASK = _Flag('instruction_mask', 'INSN_MASK_')
363
364 CONFIGURATION_IDS = _Enum('configuration_ids', 'INSN_CONFIG_', blacklist=[
365         '8254_set_mode'])
366
367 INSN = _Enum('instruction', 'INSN_',
368              blacklist=['mask_%s' % i.name for i in INSN_MASK] + [
369         'config_%s' % i.name for i in CONFIGURATION_IDS])
370
371 TRIG = _Flag('trigger_flags', 'TRIG_', whitelist=[
372         'bogus', 'dither', 'deglitch', 'config', 'wake_eos'])
373 TRIG.bogus.doc += ' (do the motions)'
374 TRIG.config.doc += ' (perform configuration, not triggering)'
375 TRIG.wake_eos.doc += ' (wake up on end-of-scan events)'
376
377 CMDF = _Flag('command_flags', 'CMDF_')
378 CMDF.priority.doc += (
379     ' (try to use a real-time interrupt while performing command)')
380
381 EV = _Flag('??', 'COMEDI_EV_')
382
383 TRIG_ROUND = _Enum('trigger_round', 'TRIG_ROUND_', blacklist=['mask'])
384 TRIG_ROUND.mask = _comedi.TRIG_ROUND_MASK
385
386 TRIG_SRC = _Flag('trigger_source_flags', 'TRIG_',
387                  blacklist=[i.name for i in TRIG] + [
388             'round_%s' % i.name for i in TRIG_ROUND] + [
389         'round_mask', 'rt', 'write'])
390 TRIG_SRC.none.doc += ' (never trigger)'
391 TRIG_SRC.now.doc += ' (trigger now + N ns)'
392 TRIG_SRC.follow.doc += ' (trigger on next lower level trig)'
393 TRIG_SRC.time.doc += ' (trigger at time N ns)'
394 TRIG_SRC.timer.doc += ' (trigger at rate N ns)'
395 TRIG_SRC.count.doc += ' (trigger when count reaches N)'
396 TRIG_SRC.ext.doc += ' (trigger on external signal N)'
397 TRIG_SRC.int.doc += ' (trigger on comedi-internal signal N)'
398 TRIG_SRC.other.doc += ' (driver defined)'
399
400 SDF_PWM = _Flag('pulse_width_modulation_subdevice_flags', 'SDF_PWM_')
401 SDF_PWM.counter.doc += ' (PWM can automatically switch off)'
402 SDF_PWM.hbridge.doc += ' (PWM is signed (H-bridge))'
403
404 SDF = _Flag('subdevice_flags', 'SDF_', blacklist=[
405         'pwm_%s' % i.name for i in SDF_PWM] + [
406         'cmd', 'writeable', 'rt'])
407 SDF.busy.doc += ' (device is busy)'
408 SDF.busy_owner.doc += ' (device is busy with your job)'
409 SDF.locked.doc += ' (subdevice is locked)'
410 SDF.lock_owner.doc += ' (you own lock)'
411 SDF.maxdata.doc += ' (maxdata depends on channel)'
412 SDF.flags.doc += ' (flags depend on channel (BROKEN))'
413 SDF.rangetype.doc += ' (range type depends on channel)'
414 SDF.soft_calibrated.doc += ' (subdevice uses software calibration)'
415 SDF.cmd_write.doc += ' (can do output commands)'
416 SDF.cmd_read.doc += ' (can to input commands)'
417 SDF.readable.doc += ' (subdevice can be read, e.g. analog input)'
418 SDF.writable.doc += ' (subdevice can be written, e.g. analog output)'
419 SDF.internal.doc += ' (subdevice does not have externally visible lines)'
420 SDF.ground.doc += ' (can do aref=ground)'
421 SDF.common.doc += ' (can do aref=common)'
422 SDF.diff.doc += ' (can do aref=diff)'
423 SDF.other.doc += ' (can do aref=other)'
424 SDF.dither.doc += ' (can do dithering)'
425 SDF.deglitch.doc += ' (can do deglitching)'
426 SDF.mmap.doc += ' (can do mmap())'
427 SDF.running.doc += ' (subdevice is acquiring data)'
428 SDF.lsampl.doc += ' (subdevice uses 32-bit samples)'
429 SDF.packed.doc += ' (subdevice can do packed DIO)'
430
431 SUBDEVICE_TYPE = _Enum('subdevice_type', 'COMEDI_SUBD_')
432 SUBDEVICE_TYPE.unused.doc += ' (unused by driver)'
433 SUBDEVICE_TYPE.ai.doc += ' (analog input)'
434 SUBDEVICE_TYPE.ao.doc += ' (analog output)'
435 SUBDEVICE_TYPE.di.doc += ' (digital input)'
436 SUBDEVICE_TYPE.do.doc += ' (digital output)'
437 SUBDEVICE_TYPE.dio.doc += ' (digital input/output)'
438 SUBDEVICE_TYPE.memory.doc += ' (memory, EEPROM, DPRAM)'
439 SUBDEVICE_TYPE.calib.doc += ' (calibration DACs)'
440 SUBDEVICE_TYPE.proc.doc += ' (processor, DSP)'
441 SUBDEVICE_TYPE.serial.doc += ' (serial IO)'
442 SUBDEVICE_TYPE.pwm.doc += ' (pulse-with modulation)'
443
444 IO_DIRECTION = _Enum('io_direction', 'COMEDI_', whitelist=[
445         'input', 'output', 'opendrain'])
446
447 SUPPORT_LEVEL = _Enum('support_level', 'COMEDI_', whitelist=[
448         'unknown_support', 'supported', 'unsupported'])
449
450 UNIT = _Enum('unit', 'UNIT_', translation={'mA':'mA'})
451 # The mA translation avoids lowercasing to 'ma'.
452 UNIT.append(_NamedInt(
453         name='external',
454         value=_comedi.RF_EXTERNAL, 
455         doc=('RF_EXTERNAL (value unit is defined by an external reference '
456              'channel)')))
457
458 CALLBACK = _Enum('callback_flags', 'COMEDI_CB_', blacklist=['block', 'eobuf'])
459 CALLBACK.eos.doc += ' (end of scan)'
460 CALLBACK.eoa.doc += ' (end of acquisition)'
461 CALLBACK.error.doc += ' (card error during acquisition)'
462 CALLBACK.overflow.doc += ' (buffer overflow/underflow)'
463
464 CONVERSION_DIRECTION = _Enum('conversion_direction', 'COMEDI_', whitelist=[
465         'to_physical', 'from_physical'])
466
467 # The following constants aren't declared in comedi.h or comedilib.h,
468 # but they should be.
469
470 LOGLEVEL = _Enum('log level', '', whitelist=[''])
471 LOGLEVEL.append(_NamedInt('silent', 0, doc='Comedilib prints nothing.'))
472 LOGLEVEL.append(_NamedInt('bug', 1, doc=(
473             'Comedilib prints error messages when there is a self-consistency '
474             'error (i.e., an internal bug (default).')))
475 LOGLEVEL.append(_NamedInt('invalid', 2, doc=(
476             'Comedilib prints an error message when an invalid parameter is '
477             'passed.')))
478 LOGLEVEL.append(_NamedInt('error', 3, doc=(
479             'Comedilib prints an error message whenever an error is generated '
480             'in the Comedilib library or in the C library, when called by '
481             'Comedilib.')))
482 LOGLEVEL.append(_NamedInt('debug', 4, doc='Comedilib prints a lot of junk.'))