1 # Copyright (C) 2011-2012 W. Trevor King <wking@drexel.edu>
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()
51 >>> d.get_version_code()
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.
143 This is a kernel-module level property, but a valid device is
144 necessary to communicate with the kernel module.
146 Returns a tuple of version numbers, e.g. `(0, 7, 61)`.
148 version = _comedilib_h.comedi_get_version_code(self.device)
150 _error.raise_error(function_name='comedi_get_version_code',
154 ret.insert(0, version & (2**8-1))
155 version >>= 2**8 # shift over 8 bits
158 def get_driver_name(self):
160 ret = _comedilib_h.comedi_get_driver_name(self.device)
162 _error.raise_error(function_name='comedi_get_driver_name',
166 def get_board_name(self):
168 ret = _comedilib_h.comedi_get_board_name(self.device)
170 _error.raise_error(function_name='comedi_get_driver_name',
174 def _get_read_subdevice(self):
175 "Find streaming input subdevice index"
176 ret = _comedilib_h.comedi_get_read_subdevice(self.device)
178 _error.raise_error(function_name='comedi_get_read_subdevice',
182 def get_read_subdevice(self, **kwargs):
183 "Find streaming input subdevice"
184 return self.subdevice(self._get_read_subdevice(), **kwargs)
186 def _get_write_subdevice(self):
187 "Find streaming output subdevice index"
188 ret = _comedilib_h.comedi_get_write_subdevice(self.device)
190 _error.raise_error(function_name='comedi_get_write_subdevice',
194 def get_write_subdevice(self, **kwargs):
195 "Find streaming output subdevice"
196 return self.subdevice(self._get_write_subdevice(), **kwargs)
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
203 _error.raise_error(function_name='comedi_find_subdevice_by_type',
207 def find_subdevice_by_type(self, subdevice_type, **kwargs):
208 """Search for a subdevice by type `subdevice_type`)."
210 `subdevice_type` should be an item from `constant.SUBDEVICE_TYPE`.
212 return self.subdevice(
213 self._find_subdevice_by_type(subdevice_type), **kwargs)
215 cpdef do_insnlist(self, insnlist):
216 """Perform multiple instructions
218 Returns the number of successfully completed instructions.
220 cdef _comedi_h.comedi_insnlist il
222 il.n_insns = len(insnlist)
225 il.insns = <_comedi_h.comedi_insn *>_stdlib.malloc(
226 il.n_insns*sizeof(_comedi_h.comedi_insn))
228 raise _PyComediError('out of memory?')
230 for j,insn in enumerate(insnlist):
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)
238 _stdlib.free(il.insns)
240 _error.raise_error(function_name='comedi_do_insnlist', ret=ret)
243 cpdef do_insn(self, _Insn insn):
244 """Preform a single instruction.
246 Returns an instruction-specific integer.
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(
256 _error.raise_error(function_name='comedi_do_insn', ret=ret)
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)
266 function_name='comedi_get_default_calibration_path')
269 # extensions to make a more idomatic Python interface
274 def subdevices(self, **kwargs):
275 "Iterate through all available subdevices."
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))
283 def subdevice(self, index, factory=_Subdevice, **kwargs):
284 return factory(device=self, index=index, **kwargs)