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 calibration import Calibration as _Calibration
28 from instruction cimport Insn as _Insn
29 from instruction import Insn as _Insn
30 from subdevice import Subdevice as _Subdevice
33 cdef class Device (object):
36 >>> from . import constant
38 >>> d = Device('/dev/comediX')
42 > d.open() # TODO: re-enable when there is a way to clear comedi_errno
43 Traceback (most recent call last):
45 PyComediError: comedi_open (/dev/comediX): No such file or directory (None)
46 >>> d.filename = '/dev/comedi0'
50 >>> d.get_n_subdevices()
54 >>> d.get_driver_name()
56 >>> s = d.get_read_subdevice()
59 >>> s = d.get_write_subdevice()
62 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.calib)
65 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.pwm)
66 Traceback (most recent call last):
68 PyComediError: comedi_find_subdevice_by_type: Success (-1)
70 As a test instruction, we'll get the time of day, which fills in
71 the data field with `[seconds, microseconds]`.
74 >>> insn.insn = constant.INSN.gtod
75 >>> insn.data = [0, 0] # space where the time value will be stored
80 chanspec: <ChanSpec chan:0 range:0 aref:ground flags:->
83 >>> print insn.data # doctest: +SKIP
85 >>> insn.data = [0, 0]
86 >>> d.do_insnlist([insn])
88 >>> print insn.data # doctest: +SKIP
91 >>> d.get_default_calibration_path()
92 '/var/lib/comedi/calibrations/ni_pcimio_pci-6052e_comedi0'
94 >>> list(d.subdevices()) # doctest: +ELLIPSIS
95 [<pycomedi.subdevice.Subdevice object at 0x...>,...]
104 def __init__(self, filename):
105 super(Device, self).__init__()
106 self.filename = filename
110 self.device = _comedilib_h.comedi_open(self.filename)
111 if self.device == NULL:
112 _error.raise_error(function_name='comedi_open',
113 error_msg=self.filename)
114 self.file = _os.fdopen(self.fileno(), 'r+')
120 ret = _comedilib_h.comedi_close(self.device)
122 _error.raise_error(function_name='comedi_close', ret=ret)
127 "File descriptor for this device"
128 ret = _comedilib_h.comedi_fileno(self.device)
130 _error.raise_error(function_name='comedi_fileno', ret=ret)
133 def get_n_subdevices(self):
134 "Number of subdevices"
135 ret = _comedilib_h.comedi_get_n_subdevices(self.device)
137 _error.raise_error(function_name='comedi_get_n_subdevices',
141 def get_version_code(self):
142 """Comedi version code as a single integer.
144 This is a kernel-module level property, but a valid device is
145 necessary to communicate with the kernel module.
147 version = _comedilib_h.comedi_get_version_code(self.device)
149 _error.raise_error(function_name='comedi_get_version_code',
153 def get_version(self):
154 """Comedi version as a tuple of version numbers.
156 Returns the result of `.get_version_code()`, but rephrased as
157 a tuple of version numbers, e.g. `(0, 7, 61)`.
159 version = self.get_version_code()
162 ret.insert(0, version & 0xff) # grab lowest 8 bits
163 version >>= 8 # shift over 8 bits
166 def get_driver_name(self):
168 ret = _comedilib_h.comedi_get_driver_name(self.device)
170 _error.raise_error(function_name='comedi_get_driver_name',
174 def get_board_name(self):
176 ret = _comedilib_h.comedi_get_board_name(self.device)
178 _error.raise_error(function_name='comedi_get_driver_name',
182 def _get_read_subdevice(self):
183 "Find streaming input subdevice index"
184 ret = _comedilib_h.comedi_get_read_subdevice(self.device)
186 _error.raise_error(function_name='comedi_get_read_subdevice',
190 def get_read_subdevice(self, **kwargs):
191 "Find streaming input subdevice"
192 return self.subdevice(self._get_read_subdevice(), **kwargs)
194 def _get_write_subdevice(self):
195 "Find streaming output subdevice index"
196 ret = _comedilib_h.comedi_get_write_subdevice(self.device)
198 _error.raise_error(function_name='comedi_get_write_subdevice',
202 def get_write_subdevice(self, **kwargs):
203 "Find streaming output subdevice"
204 return self.subdevice(self._get_write_subdevice(), **kwargs)
206 def _find_subdevice_by_type(self, subdevice_type):
207 "Search for a subdevice index for type `subdevice_type`)."
208 ret = _comedilib_h.comedi_find_subdevice_by_type(
209 self.device, subdevice_type.value, 0) # 0 is starting subdevice
211 _error.raise_error(function_name='comedi_find_subdevice_by_type',
215 def find_subdevice_by_type(self, subdevice_type, **kwargs):
216 """Search for a subdevice by type `subdevice_type`)."
218 `subdevice_type` should be an item from `constant.SUBDEVICE_TYPE`.
220 return self.subdevice(
221 self._find_subdevice_by_type(subdevice_type), **kwargs)
223 cpdef do_insnlist(self, insnlist):
224 """Perform multiple instructions
226 Returns the number of successfully completed instructions.
228 cdef _comedi_h.comedi_insnlist il
230 il.n_insns = len(insnlist)
233 il.insns = <_comedi_h.comedi_insn *>_stdlib.malloc(
234 il.n_insns*sizeof(_comedi_h.comedi_insn))
236 raise _PyComediError('out of memory?')
238 for j,insn in enumerate(insnlist):
240 # By copying the pointer to data, changes to this
241 # copied instruction will also affect the original
242 # instruction's data.
243 il.insns[j] = i.get_comedi_insn()
244 ret = _comedilib_h.comedi_do_insnlist(self.device, &il)
246 _stdlib.free(il.insns)
247 if ret < len(insnlist):
248 _error.raise_error(function_name='comedi_do_insnlist', ret=ret)
251 cpdef do_insn(self, _Insn insn):
252 """Preform a single instruction.
254 Returns an instruction-specific integer.
256 cdef _comedi_h.comedi_insn i
257 # By copying the pointer to data, changes to this
258 # copied instruction will also affect the original
259 # instruction's data.
260 i = insn.get_comedi_insn()
261 ret = _comedilib_h.comedi_do_insn(
264 _error.raise_error(function_name='comedi_do_insn', ret=ret)
267 def get_default_calibration_path(self):
268 "The default calibration path for this device"
269 assert self.device != NULL, (
270 'must call get_default_calibration_path on an open device.')
271 ret = _comedilib_h.comedi_get_default_calibration_path(self.device)
274 function_name='comedi_get_default_calibration_path')
277 def parse_calibration(self, path=None):
278 """The soft calibration from a file for this device.
280 If path is None, the default calibration file is used.
283 path = self.get_default_calibration_path()
284 c = _Calibration(device=self)
288 # extensions to make a more idomatic Python interface
293 def subdevices(self, **kwargs):
294 "Iterate through all available subdevices."
296 for i in range(self.get_n_subdevices()):
297 #yield self.subdevice(i, **kwargs)
298 # Generators are not supported in Cython 0.14.1
299 ret.append(self.subdevice(i, **kwargs))
302 def subdevice(self, index, factory=_Subdevice, **kwargs):
303 return factory(device=self, index=index, **kwargs)