Ran update-copyright.py.
[pycomedi.git] / pycomedi / device.pyx
1 # Copyright (C) 2011-2012 W. Trevor King <wking@drexel.edu>
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 "Wrap device-wide Comedi functions in a `Device` class"
18
19 import os as _os
20 cimport libc.stdlib as _stdlib
21
22 from pycomedi import LOG as _LOG
23 from pycomedi import PyComediError as _PyComediError
24 cimport _comedi_h
25 cimport _comedilib_h
26 import _error
27 from instruction cimport Insn as _Insn
28 from instruction import Insn as _Insn
29 from subdevice import Subdevice as _Subdevice
30
31
32 cdef class Device (object):
33     """A Comedi device
34
35     >>> from . import constant
36
37     >>> d = Device('/dev/comediX')
38     >>> d.filename
39     '/dev/comediX'
40
41     > d.open()  # TODO: re-enable when there is a way to clear comedi_errno
42     Traceback (most recent call last):
43       ...
44     PyComediError: comedi_open (/dev/comediX): No such file or directory (None)
45     >>> d.filename = '/dev/comedi0'
46     >>> d.open()
47     >>> d.fileno()
48     3
49     >>> d.get_n_subdevices()
50     14
51     >>> d.get_version_code()
52     (0, 0, 76)
53     >>> d.get_driver_name()
54     'ni_pcimio'
55     >>> s = d.get_read_subdevice()
56     >>> s.index
57     0
58     >>> s = d.get_write_subdevice()
59     >>> s.index
60     1
61     >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.calib)
62     >>> s.index
63     5
64     >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.pwm)
65     Traceback (most recent call last):
66       ...
67     PyComediError: comedi_find_subdevice_by_type: Success (-1)
68
69     As a test instruction, we'll get the time of day, which fills in
70     the data field with `[seconds, microseconds]`.
71
72     >>> insn = d.insn()
73     >>> insn.insn = constant.INSN.gtod
74     >>> insn.data = [0, 0]  # space where the time value will be stored
75     >>> print str(insn)
76         insn: gtod
77         data: [0 0]
78       subdev: 0
79     chanspec: <ChanSpec chan:0 range:0 aref:ground flags:->
80     >>> d.do_insn(insn)
81     2
82     >>> print insn.data  # doctest: +SKIP
83     [1297377578     105790]
84     >>> insn.data = [0, 0]
85     >>> d.do_insnlist([insn])
86     1
87     >>> print insn.data  # doctest: +SKIP
88     [1297377578     110559]
89
90     >>> d.get_default_calibration_path()
91     '/var/lib/comedi/calibrations/ni_pcimio_pci-6052e_comedi0'
92
93     >>> list(d.subdevices())  # doctest: +ELLIPSIS
94     [<pycomedi.subdevice.Subdevice object at 0x...>,...]
95
96     >>> d.close()
97     """
98     def __cinit__(self):
99         self.device = NULL
100         self.file = None
101         self.filename = None
102
103     def __init__(self, filename):
104         super(Device, self).__init__()
105         self.filename = filename
106
107     def open(self):
108         "Open device"
109         self.device = _comedilib_h.comedi_open(self.filename)
110         if self.device == NULL:
111             _error.raise_error(function_name='comedi_open',
112                                error_msg=self.filename)
113         self.file = _os.fdopen(self.fileno(), 'r+')
114
115     def close(self):
116         "Close device"
117         self.file.flush()
118         self.file.close()
119         ret = _comedilib_h.comedi_close(self.device)
120         if ret < 0:
121             _error.raise_error(function_name='comedi_close', ret=ret)
122         self.device = NULL
123         self.file = None
124
125     def fileno(self):
126         "File descriptor for this device"
127         ret = _comedilib_h.comedi_fileno(self.device)
128         if ret < 0:
129             _error.raise_error(function_name='comedi_fileno', ret=ret)
130         return ret
131
132     def get_n_subdevices(self):
133         "Number of subdevices"
134         ret = _comedilib_h.comedi_get_n_subdevices(self.device)
135         if ret < 0:
136             _error.raise_error(function_name='comedi_get_n_subdevices',
137                                 ret=ret)
138         return ret
139
140     def get_version_code(self):
141         """Comedi version code.
142
143         This is a kernel-module level property, but a valid device is
144         necessary to communicate with the kernel module.
145
146         Returns a tuple of version numbers, e.g. `(0, 7, 61)`.
147         """
148         version = _comedilib_h.comedi_get_version_code(self.device)
149         if version < 0:
150             _error.raise_error(function_name='comedi_get_version_code',
151                                 ret=version)
152         ret = []
153         for i in range(3):
154             ret.insert(0, version & (2**8-1))
155             version >>= 2**8  # shift over 8 bits
156         return tuple(ret)
157
158     def get_driver_name(self):
159         "Comedi driver name"
160         ret = _comedilib_h.comedi_get_driver_name(self.device)
161         if ret == NULL:
162             _error.raise_error(function_name='comedi_get_driver_name',
163                                 ret=ret)
164         return ret
165
166     def get_board_name(self):
167         "Comedi board name"
168         ret = _comedilib_h.comedi_get_board_name(self.device)
169         if ret == NULL:
170             _error.raise_error(function_name='comedi_get_driver_name',
171                                 ret=ret)
172         return ret
173
174     def _get_read_subdevice(self):
175         "Find streaming input subdevice index"
176         ret = _comedilib_h.comedi_get_read_subdevice(self.device)
177         if ret < 0:
178             _error.raise_error(function_name='comedi_get_read_subdevice',
179                                 ret=ret)
180         return ret
181
182     def get_read_subdevice(self, **kwargs):
183         "Find streaming input subdevice"
184         return self.subdevice(self._get_read_subdevice(), **kwargs)
185
186     def _get_write_subdevice(self):
187         "Find streaming output subdevice index"
188         ret = _comedilib_h.comedi_get_write_subdevice(self.device)
189         if ret < 0:
190             _error.raise_error(function_name='comedi_get_write_subdevice',
191                                 ret=ret)
192         return ret
193
194     def get_write_subdevice(self, **kwargs):
195         "Find streaming output subdevice"
196         return self.subdevice(self._get_write_subdevice(), **kwargs)
197
198     def _find_subdevice_by_type(self, subdevice_type):
199         "Search for a subdevice index for type `subdevice_type`)."
200         ret = _comedilib_h.comedi_find_subdevice_by_type(
201             self.device, subdevice_type.value, 0)  # 0 is starting subdevice
202         if ret < 0:
203             _error.raise_error(function_name='comedi_find_subdevice_by_type',
204                                 ret=ret)
205         return ret
206
207     def find_subdevice_by_type(self, subdevice_type, **kwargs):
208         """Search for a subdevice by type `subdevice_type`)."
209
210         `subdevice_type` should be an item from `constant.SUBDEVICE_TYPE`.
211         """
212         return self.subdevice(
213             self._find_subdevice_by_type(subdevice_type), **kwargs)
214
215     cpdef do_insnlist(self, insnlist):
216         """Perform multiple instructions
217
218         Returns the number of successfully completed instructions.
219         """
220         cdef _comedi_h.comedi_insnlist il
221         cdef _Insn i
222         il.n_insns = len(insnlist)
223         if il.n_insns == 0:
224             return
225         il.insns = <_comedi_h.comedi_insn *>_stdlib.malloc(
226             il.n_insns*sizeof(_comedi_h.comedi_insn))
227         if il.insns is NULL:
228             raise _PyComediError('out of memory?')
229         try:
230             for j,insn in enumerate(insnlist):
231                 i = insn
232                 # By copying the pointer to data, changes to this
233                 # copied instruction will also affect the original
234                 # instruction's data.
235                 il.insns[j] = i.get_comedi_insn()
236             ret = _comedilib_h.comedi_do_insnlist(self.device, &il)
237         finally:
238             _stdlib.free(il.insns)
239         if ret < 0:
240             _error.raise_error(function_name='comedi_do_insnlist', ret=ret)
241         return ret
242
243     cpdef do_insn(self, _Insn insn):
244         """Preform a single instruction.
245
246         Returns an instruction-specific integer.
247         """
248         cdef _comedi_h.comedi_insn i
249         # By copying the pointer to data, changes to this
250         # copied instruction will also affect the original
251         # instruction's data.
252         i = insn.get_comedi_insn()
253         ret = _comedilib_h.comedi_do_insn(
254             self.device, &i)
255         if ret < 0:
256             _error.raise_error(function_name='comedi_do_insn', ret=ret)
257         return ret
258
259     def get_default_calibration_path(self):
260         "The default calibration path for this device"
261         assert self.device != NULL, (
262             'must call get_default_calibration_path on an open device.')
263         ret = _comedilib_h.comedi_get_default_calibration_path(self.device)
264         if ret == NULL:
265             _error.raise_error(
266                 function_name='comedi_get_default_calibration_path')
267         return ret
268
269     # extensions to make a more idomatic Python interface
270
271     def insn(self):
272         return _Insn()
273
274     def subdevices(self, **kwargs):
275         "Iterate through all available subdevices."
276         ret = []
277         for i in range(self.get_n_subdevices()):
278             #yield self.subdevice(i, **kwargs)
279             # Generators are not supported in Cython 0.14.1
280             ret.append(self.subdevice(i, **kwargs))
281         return ret
282
283     def subdevice(self, index, factory=_Subdevice, **kwargs):
284         return factory(device=self, index=index, **kwargs)