1 # Copyright (C) 2011-2012 W. Trevor King <wking@tremily.us>
3 # This file is part of pycomedi.
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
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.
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/>.
17 "Wrap device-wide Comedi functions in a `Device` class"
20 cimport libc.stdlib as _stdlib
22 from pycomedi import LOG as _LOG
23 from pycomedi import PyComediError as _PyComediError
27 from instruction cimport Insn as _Insn
28 from instruction import Insn as _Insn
29 from subdevice import Subdevice as _Subdevice
32 cdef class Device (object):
35 >>> from . import constant
37 >>> d = Device('/dev/comediX')
41 > d.open() # TODO: re-enable when there is a way to clear comedi_errno
42 Traceback (most recent call last):
44 PyComediError: comedi_open (/dev/comediX): No such file or directory (None)
45 >>> d.filename = '/dev/comedi0'
49 >>> d.get_n_subdevices()
53 >>> d.get_driver_name()
55 >>> s = d.get_read_subdevice()
58 >>> s = d.get_write_subdevice()
61 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.calib)
64 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.pwm)
65 Traceback (most recent call last):
67 PyComediError: comedi_find_subdevice_by_type: Success (-1)
69 As a test instruction, we'll get the time of day, which fills in
70 the data field with `[seconds, microseconds]`.
73 >>> insn.insn = constant.INSN.gtod
74 >>> insn.data = [0, 0] # space where the time value will be stored
79 chanspec: <ChanSpec chan:0 range:0 aref:ground flags:->
82 >>> print insn.data # doctest: +SKIP
84 >>> insn.data = [0, 0]
85 >>> d.do_insnlist([insn])
87 >>> print insn.data # doctest: +SKIP
90 >>> d.get_default_calibration_path()
91 '/var/lib/comedi/calibrations/ni_pcimio_pci-6052e_comedi0'
93 >>> list(d.subdevices()) # doctest: +ELLIPSIS
94 [<pycomedi.subdevice.Subdevice object at 0x...>,...]
103 def __init__(self, filename):
104 super(Device, self).__init__()
105 self.filename = filename
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+')
119 ret = _comedilib_h.comedi_close(self.device)
121 _error.raise_error(function_name='comedi_close', ret=ret)
126 "File descriptor for this device"
127 ret = _comedilib_h.comedi_fileno(self.device)
129 _error.raise_error(function_name='comedi_fileno', ret=ret)
132 def get_n_subdevices(self):
133 "Number of subdevices"
134 ret = _comedilib_h.comedi_get_n_subdevices(self.device)
136 _error.raise_error(function_name='comedi_get_n_subdevices',
140 def get_version_code(self):
141 """Comedi version code as a single integer.
143 This is a kernel-module level property, but a valid device is
144 necessary to communicate with the kernel module.
146 version = _comedilib_h.comedi_get_version_code(self.device)
148 _error.raise_error(function_name='comedi_get_version_code',
152 def get_version(self):
153 """Comedi version as a tuple of version numbers.
155 Returns the result of `.get_version_code()`, but rephrased as
156 a tuple of version numbers, e.g. `(0, 7, 61)`.
158 version = self.get_version_code()
161 ret.insert(0, version & 0xff) # grab lowest 8 bits
162 version >>= 8 # shift over 8 bits
165 def get_driver_name(self):
167 ret = _comedilib_h.comedi_get_driver_name(self.device)
169 _error.raise_error(function_name='comedi_get_driver_name',
173 def get_board_name(self):
175 ret = _comedilib_h.comedi_get_board_name(self.device)
177 _error.raise_error(function_name='comedi_get_driver_name',
181 def _get_read_subdevice(self):
182 "Find streaming input subdevice index"
183 ret = _comedilib_h.comedi_get_read_subdevice(self.device)
185 _error.raise_error(function_name='comedi_get_read_subdevice',
189 def get_read_subdevice(self, **kwargs):
190 "Find streaming input subdevice"
191 return self.subdevice(self._get_read_subdevice(), **kwargs)
193 def _get_write_subdevice(self):
194 "Find streaming output subdevice index"
195 ret = _comedilib_h.comedi_get_write_subdevice(self.device)
197 _error.raise_error(function_name='comedi_get_write_subdevice',
201 def get_write_subdevice(self, **kwargs):
202 "Find streaming output subdevice"
203 return self.subdevice(self._get_write_subdevice(), **kwargs)
205 def _find_subdevice_by_type(self, subdevice_type):
206 "Search for a subdevice index for type `subdevice_type`)."
207 ret = _comedilib_h.comedi_find_subdevice_by_type(
208 self.device, subdevice_type.value, 0) # 0 is starting subdevice
210 _error.raise_error(function_name='comedi_find_subdevice_by_type',
214 def find_subdevice_by_type(self, subdevice_type, **kwargs):
215 """Search for a subdevice by type `subdevice_type`)."
217 `subdevice_type` should be an item from `constant.SUBDEVICE_TYPE`.
219 return self.subdevice(
220 self._find_subdevice_by_type(subdevice_type), **kwargs)
222 cpdef do_insnlist(self, insnlist):
223 """Perform multiple instructions
225 Returns the number of successfully completed instructions.
227 cdef _comedi_h.comedi_insnlist il
229 il.n_insns = len(insnlist)
232 il.insns = <_comedi_h.comedi_insn *>_stdlib.malloc(
233 il.n_insns*sizeof(_comedi_h.comedi_insn))
235 raise _PyComediError('out of memory?')
237 for j,insn in enumerate(insnlist):
239 # By copying the pointer to data, changes to this
240 # copied instruction will also affect the original
241 # instruction's data.
242 il.insns[j] = i.get_comedi_insn()
243 ret = _comedilib_h.comedi_do_insnlist(self.device, &il)
245 _stdlib.free(il.insns)
246 if ret < len(insnlist):
247 _error.raise_error(function_name='comedi_do_insnlist', ret=ret)
250 cpdef do_insn(self, _Insn insn):
251 """Preform a single instruction.
253 Returns an instruction-specific integer.
255 cdef _comedi_h.comedi_insn i
256 # By copying the pointer to data, changes to this
257 # copied instruction will also affect the original
258 # instruction's data.
259 i = insn.get_comedi_insn()
260 ret = _comedilib_h.comedi_do_insn(
263 _error.raise_error(function_name='comedi_do_insn', ret=ret)
266 def get_default_calibration_path(self):
267 "The default calibration path for this device"
268 assert self.device != NULL, (
269 'must call get_default_calibration_path on an open device.')
270 ret = _comedilib_h.comedi_get_default_calibration_path(self.device)
273 function_name='comedi_get_default_calibration_path')
276 # extensions to make a more idomatic Python interface
281 def subdevices(self, **kwargs):
282 "Iterate through all available subdevices."
284 for i in range(self.get_n_subdevices()):
285 #yield self.subdevice(i, **kwargs)
286 # Generators are not supported in Cython 0.14.1
287 ret.append(self.subdevice(i, **kwargs))
290 def subdevice(self, index, factory=_Subdevice, **kwargs):
291 return factory(device=self, index=index, **kwargs)