Convert to more generic Pythonic wrapper. Not everything works yet.
[pycomedi.git] / pycomedi / constants.py
1 # Copyright (C) 2010  W. Trevor King
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 """Enums and flags are bundled into class instances for easier browsing.
17
18 >>> SUBDEVICE_TYPE  # doctest: +NORMALIZE_WHITESPACE
19 [<_NamedInt unused>, <_NamedInt ai>, <_NamedInt ao>, <_NamedInt di>,
20  <_NamedInt do>, <_NamedInt dio>, <_NamedInt counter>, <_NamedInt timer>,
21  <_NamedInt memory>, <_NamedInt calib>, <_NamedInt proc>, <_NamedInt serial>,
22  <_NamedInt pwm>]
23 >>> SUBDEVICE_TYPE.dio
24 <_NamedInt dio>
25 >>> SUBDEVICE_TYPE.dio.value == _comedi.COMEDI_SUBD_DIO
26 True
27 >>> SUBDEVICE_TYPE.dio.doc
28 'COMEDI_SUBD_DIO (digital input/output)'
29
30 You can also search by name or value.
31
32 >>> TRIG_SRC.index_by_name('timer')
33 <_NamedInt timer>
34 >>> TRIG_SRC.index_by_value(_comedi.TRIG_NOW)
35 <_NamedInt now>
36
37 Some flags have constants for setting or clearing all the flags at once.
38
39 >>> TRIG_SRC  # doctest: +NORMALIZE_WHITESPACE
40 [<_NamedInt none>, <_NamedInt now>, <_NamedInt follow>, <_NamedInt time>,
41  <_NamedInt timer>, <_NamedInt count>, <_NamedInt ext>, <_NamedInt int>,
42  <_NamedInt other>]
43 >>> TRIG_SRC._empty
44 <_NamedInt invalid>
45 >>> TRIG_SRC._all
46 <_NamedInt any>
47
48 Flag instances have a special wrapper that stores their value.
49
50 >>> f = FlagValue(SDF, 17)
51 >>> f.flag  # doctest: +ELLIPSIS
52 [<_NamedInt busy>, <_NamedInt busy_owner>, ...]
53 >>> f.busy
54 True
55 >>> f.busy = False
56 >>> f._value
57 16
58
59 You can treat named integers as Python integers with bitwise operations,
60
61 >>> a = TRIG_SRC.now | TRIG_SRC.follow | TRIG_SRC.time | 64
62 >>> a
63 <_BitwiseOperator 78>
64 >>> a.value
65 78
66 >>> TRIG_SRC.none & TRIG_SRC.now
67 <_BitwiseOperator 0>
68
69 But because of the way Python operator overloading works, plain
70 integers must go at the end of bitwise chains.
71
72 >>> 64 | TRIG_SRC.now
73 Traceback (most recent call last):
74   ...
75 TypeError: unsupported operand type(s) for |: 'int' and '_NamedInt'
76 """
77
78 from math import log as _log
79
80 import comedi as _comedi
81
82
83 class _BitwiseOperator (object):
84     def __init__(self, value):
85         self.value = value
86
87     def __str__(self):
88         return self.__repr__()
89
90     def __repr__(self):
91         return '<%s %s>' % (self.__class__.__name__, self.value)
92
93     def __and__(self, other):
94         "Bitwise and acts on `_BitwiseOperator.value`."
95         if isinstance(other, _BitwiseOperator):
96             other = other.value
97         return _BitwiseOperator(int.__and__(self.value, other))
98
99     def __or__(self, other):
100         "Bitwise or acts on `_BitwiseOperator.value`."
101         if isinstance(other, _BitwiseOperator):
102             other = other.value
103         return _BitwiseOperator(int.__or__(self.value, other))
104
105
106 class _NamedInt (_BitwiseOperator):
107     "A flag or enum item."
108     def __init__(self, name, value, doc=None):
109         super(_NamedInt, self).__init__(value)
110         self.name = name
111         self.doc = doc
112
113     def __str__(self):
114         return self.name
115
116     def __repr__(self):
117         return '<%s %s>' % (self.__class__.__name__, self.name)
118
119
120 class _Enum (list):
121     "An enumerated list"
122     def __init__(self, name, prefix, blacklist=None, whitelist=None,
123                  translation=None):
124         super(_Enum, self).__init__()
125         self.name = name
126         if blacklist == None:
127             blacklist = []
128         if translation == None:
129             translation = {}
130         self._name_keys = {}
131         self._value_keys = {}
132         for attr in dir(_comedi):
133             if attr.startswith(prefix):
134                 item_name = self._item_name(attr, prefix, translation)
135                 if self._is_ignored(item_name, blacklist, whitelist):
136                     continue
137                 self._add_item(attr, item_name)
138         self.sort(key=lambda item: item.value)
139
140     def _item_name(self, attr, prefix, translation):
141         item_name = attr[len(prefix):]
142         if item_name in translation:
143             return translation[item_name]
144         else:
145             return item_name.lower()
146
147     def _is_ignored(self, item_name, blacklist, whitelist):
148         return (item_name in blacklist
149                 or whitelist != None and item_name not in whitelist)
150
151     def _add_item(self, attr, item_name):
152         item_value = getattr(_comedi, attr)
153         item = _NamedInt(item_name, item_value, doc=attr)
154         self.append(item)
155         self._name_keys[item_name] = item
156         if item_value in self._value_keys and item_name:
157             raise ValueError('value collision in %s: %s = %s = %#x'
158                              % (self.name, item_name,
159                                 self._value_keys[item_value], item_value))
160         self._value_keys[item_value] = item
161         setattr(self, item_name, item)
162
163     def index_by_name(self, name):
164         return self._name_keys[name]
165
166     def index_by_value(self, value):
167         return self._value_keys[value]
168
169
170 class _Flag (_Enum):
171     "A flag"
172     def __init__(self, *args, **kwargs):
173         super(_Flag, self).__init__(*args, **kwargs)
174         self._empty = None
175         self._all = None
176         for flag in self:
177             if flag.value == 0:
178                 self._empty = flag
179             elif flag.value < 0 or _log(flag.value, 2) % 1 != 0:
180                 if self._all:
181                     raise ValueError(
182                         'mutliple multi-bit flags in %s: %s = %#x and %s = %#x'
183                         % (self.name, self._all.name, self._all.value,
184                            flag.name, flag.value))
185                 self._all = flag
186         if self._empty:
187             self.remove(self._empty)
188         if self._all:
189             self.remove(self._all)
190
191     def get(self, value, name):
192         flag = getattr(self, name)
193         assert flag.value != 0, '%s: %s' % (self.name, flag)
194         return value & flag.value == flag.value
195
196     def set(self, value, name, status):
197         flag = getattr(self, name)
198         if status == True:
199             return value | flag.value
200         return (value | flag.value) - flag.value
201
202
203 class FlagValue (object):
204     """A flag instance (flag + value)
205
206     Examples
207     --------
208
209     >>> f = FlagValue(flag=TRIG_SRC, value=0, default='empty')
210     >>> f.any
211     False
212     >>> print f
213     empty
214     >>> f.now = True
215     >>> f.timer = True
216     >>> f.int = True
217     >>> print f
218     now|timer|int
219     """
220     def __init__(self, flag, value, default='-'):
221         self.flag = flag
222         self._value = value
223         self._default = default
224
225     def __str__(self):
226         flags = [f for f in self.flag if getattr(self, f.name)]
227         if len(flags) == 0:
228             return self._default
229         return '|'.join([f.name for f in flags])
230
231     def __getattr__(self, name):
232         return self.flag.get(self._value, name)
233
234     def __setattr__(self, name, value):
235         if name != 'flag' and not name.startswith('_'):
236             value = self.flag.set(self._value, name, value)
237             name = '_value'
238         super(FlagValue, self).__setattr__(name, value)
239
240
241 # blacklist deprecated values (and those belonging to other _Enums or _Flags)
242
243 AREF = _Enum('analog_reference', 'AREF_')
244 AREF.diff.doc += ' (differential)'
245 AREF.other.doc += ' (other / undefined)'
246
247 #GPCT = _Flag('general_purpose_counter_timer', 'GPCT_')
248 # Two competing flag sets?  Need some documentation.
249
250 INSN_MASK = _Flag('instruction_mask', 'INSN_MASK_')
251
252 CONFIGURATION_IDS = _Enum('configuration_ids', 'INSN_CONFIG_', blacklist=[
253         '8254_set_mode'])
254
255 INSN = _Enum('instruction', 'INSN_',
256              blacklist=['mask_%s' % i.name for i in INSN_MASK] + [
257         'config_%s' % i.name for i in CONFIGURATION_IDS])
258
259 TRIG = _Flag('trigger_flags', 'TRIG_', whitelist=[
260         'bogus', 'dither', 'deglitch', 'config', 'wake_eos'])
261 TRIG.bogus.doc += ' (do the motions)'
262 TRIG.config.doc += ' (perform configuration, not triggering)'
263 TRIG.wake_eos.doc += ' (wake up on end-of-scan events)'
264
265 CMDF = _Flag('command_flags', 'CMDF_')
266 CMDF.priority.doc += (
267     ' (try to use a real-time interrupt while performing command)')
268
269 EV = _Flag('??', 'COMEDI_EV_')
270
271 TRIG_ROUND = _Enum('trigger_round', 'TRIG_ROUND_', blacklist=['mask'])
272 TRIG_ROUND.mask = _comedi.TRIG_ROUND_MASK
273
274 TRIG_SRC = _Flag('trigger_source_flags', 'TRIG_',
275                  blacklist=[i.name for i in TRIG] + [
276             'round_%s' % i.name for i in TRIG_ROUND] + [
277         'round_mask', 'rt', 'write'])
278 TRIG_SRC.none.doc += ' (never trigger)'
279 TRIG_SRC.now.doc += ' (trigger now + N ns)'
280 TRIG_SRC.follow.doc += ' (trigger on next lower level trig)'
281 TRIG_SRC.time.doc += ' (trigger at time N ns)'
282 TRIG_SRC.timer.doc += ' (trigger at rate N ns)'
283 TRIG_SRC.count.doc += ' (trigger when count reaches N)'
284 TRIG_SRC.ext.doc += ' (trigger on external signal N)'
285 TRIG_SRC.int.doc += ' (trigger on comedi-internal signal N)'
286 TRIG_SRC.other.doc += ' (driver defined)'
287
288 SDF_PWM = _Flag('pulse_width_modulation_subdevice_flags', 'SDF_PWM_')
289 SDF_PWM.counter.doc += ' (PWM can automatically switch off)'
290 SDF_PWM.hbridge.doc += ' (PWM is signed (H-bridge))'
291
292 SDF = _Flag('subdevice_flags', 'SDF_', blacklist=[
293         'pwm_%s' % i.name for i in SDF_PWM] + [
294         'cmd', 'writeable', 'rt'])
295 SDF.busy.doc += ' (device is busy)'
296 SDF.busy_owner.doc += ' (device is busy with your job)'
297 SDF.locked.doc += ' (subdevice is locked)'
298 SDF.lock_owner.doc += ' (you own lock)'
299 SDF.maxdata.doc += ' (maxdata depends on channel)'
300 SDF.flags.doc += ' (flags depend on channel)'
301 SDF.rangetype.doc += ' (range type depends on channel)'
302 SDF.soft_calibrated.doc += ' (subdevice uses software calibration)'
303 SDF.cmd_write.doc += ' (can do output commands)'
304 SDF.cmd_read.doc += ' (can to input commands)'
305 SDF.readable.doc += ' (subdevice can be read, e.g. analog input)'
306 SDF.writable.doc += ' (subdevice can be written, e.g. analog output)'
307 SDF.internal.doc += ' (subdevice does not have externally visible lines)'
308 SDF.ground.doc += ' (can do aref=ground)'
309 SDF.common.doc += ' (can do aref=common)'
310 SDF.diff.doc += ' (can do aref=diff)'
311 SDF.other.doc += ' (can do aref=other)'
312 SDF.dither.doc += ' (can do dithering)'
313 SDF.deglitch.doc += ' (can do deglitching)'
314 SDF.mmap.doc += ' (can do mmap())'
315 SDF.running.doc += ' (subdevice is acquiring data)'
316 SDF.lsampl.doc += ' (subdevice uses 32-bit samples)'
317 SDF.packed.doc += ' (subdevice can do packed DIO)'
318
319 SUBDEVICE_TYPE = _Enum('subdevice_type', 'COMEDI_SUBD_')
320 SUBDEVICE_TYPE.unused.doc += ' (unused by driver)'
321 SUBDEVICE_TYPE.ai.doc += ' (analog input)'
322 SUBDEVICE_TYPE.ao.doc += ' (analog output)'
323 SUBDEVICE_TYPE.di.doc += ' (digital input)'
324 SUBDEVICE_TYPE.do.doc += ' (digital output)'
325 SUBDEVICE_TYPE.dio.doc += ' (digital input/output)'
326 SUBDEVICE_TYPE.memory.doc += ' (memory, EEPROM, DPRAM)'
327 SUBDEVICE_TYPE.calib.doc += ' (calibration DACs)'
328 SUBDEVICE_TYPE.proc.doc += ' (processor, DSP)'
329 SUBDEVICE_TYPE.serial.doc += ' (serial IO)'
330 SUBDEVICE_TYPE.pwm.doc += ' (pulse-with modulation)'
331
332 IO_DIRECTION = _Enum('io_direction', 'COMEDI_', whitelist=[
333         'input', 'output', 'opendrain'])
334
335 SUPPORT_LEVEL = _Enum('support_level', 'COMEDI_', whitelist=[
336         'unknown_support', 'supported', 'unsupported'])
337
338 UNIT = _Enum('unit', 'UNIT_', translation={'mA':'mA'})
339
340 CB = _Enum('callback_flags', 'COMEDI_CB_', blacklist=['block', 'eobuf'])
341 CB.eos.doc += ' (end of scan)'
342 CB.eoa.doc += ' (end of acquisition)'
343 CB.error.doc += ' (card error during acquisition)'
344 CB.overflow.doc += ' (buffer overflow/underflow)'