1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2011-2012 W. Trevor King <wking@tremily.us>
3 # Éric Piel <piel@delmic.com>
5 # This file is part of pycomedi.
7 # pycomedi is free software: you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation, either version 2 of the License, or (at your option) any later
12 # pycomedi is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # pycomedi. If not, see <http://www.gnu.org/licenses/>.
19 "Wrap device-wide Comedi functions in a `Device` class"
22 cimport libc.stdlib as _stdlib
24 from pycomedi import LOG as _LOG
25 from pycomedi import PyComediError as _PyComediError
29 from calibration import Calibration as _Calibration
30 from instruction cimport Insn as _Insn
31 from instruction import Insn as _Insn
32 from subdevice import Subdevice as _Subdevice
35 cdef class Device (object):
38 >>> from . import constant
40 >>> d = Device('/dev/comediX')
44 > d.open() # TODO: re-enable when there is a way to clear comedi_errno
45 Traceback (most recent call last):
47 PyComediError: comedi_open (/dev/comediX): No such file or directory (None)
48 >>> d.filename = '/dev/comedi0'
52 >>> d.get_n_subdevices()
56 >>> d.get_driver_name()
58 >>> s = d.get_read_subdevice()
61 >>> s = d.get_write_subdevice()
64 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.calib)
67 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.pwm)
68 Traceback (most recent call last):
70 PyComediError: comedi_find_subdevice_by_type: Success (-1)
72 As a test instruction, we'll get the time of day, which fills in
73 the data field with `[seconds, microseconds]`.
76 >>> insn.insn = constant.INSN.gtod
77 >>> insn.data = [0, 0] # space where the time value will be stored
82 chanspec: <ChanSpec chan:0 range:0 aref:ground flags:->
85 >>> print insn.data # doctest: +SKIP
87 >>> insn.data = [0, 0]
88 >>> d.do_insnlist([insn])
90 >>> print insn.data # doctest: +SKIP
93 >>> d.get_default_calibration_path()
94 '/var/lib/comedi/calibrations/ni_pcimio_pci-6052e_comedi0'
96 >>> list(d.subdevices()) # doctest: +ELLIPSIS
97 [<pycomedi.subdevice.Subdevice object at 0x...>,...]
106 def __init__(self, filename):
107 super(Device, self).__init__()
108 self.filename = filename
112 self.device = _comedilib_h.comedi_open(self.filename)
113 if self.device == NULL:
114 _error.raise_error(function_name='comedi_open',
115 error_msg=self.filename)
116 self.file = _os.fdopen(self.fileno(), 'r+')
122 ret = _comedilib_h.comedi_close(self.device)
124 _error.raise_error(function_name='comedi_close', ret=ret)
129 "File descriptor for this device"
130 ret = _comedilib_h.comedi_fileno(self.device)
132 _error.raise_error(function_name='comedi_fileno', ret=ret)
135 def get_n_subdevices(self):
136 "Number of subdevices"
137 ret = _comedilib_h.comedi_get_n_subdevices(self.device)
139 _error.raise_error(function_name='comedi_get_n_subdevices',
143 def get_version_code(self):
144 """Comedi version code as a single integer.
146 This is a kernel-module level property, but a valid device is
147 necessary to communicate with the kernel module.
149 version = _comedilib_h.comedi_get_version_code(self.device)
151 _error.raise_error(function_name='comedi_get_version_code',
155 def get_version(self):
156 """Comedi version as a tuple of version numbers.
158 Returns the result of `.get_version_code()`, but rephrased as
159 a tuple of version numbers, e.g. `(0, 7, 61)`.
161 version = self.get_version_code()
164 ret.insert(0, version & 0xff) # grab lowest 8 bits
165 version >>= 8 # shift over 8 bits
168 def get_driver_name(self):
170 ret = _comedilib_h.comedi_get_driver_name(self.device)
172 _error.raise_error(function_name='comedi_get_driver_name',
176 def get_board_name(self):
178 ret = _comedilib_h.comedi_get_board_name(self.device)
180 _error.raise_error(function_name='comedi_get_driver_name',
184 def _get_read_subdevice(self):
185 "Find streaming input subdevice index"
186 ret = _comedilib_h.comedi_get_read_subdevice(self.device)
188 _error.raise_error(function_name='comedi_get_read_subdevice',
192 def get_read_subdevice(self, **kwargs):
193 "Find streaming input subdevice"
194 return self.subdevice(self._get_read_subdevice(), **kwargs)
196 def _get_write_subdevice(self):
197 "Find streaming output subdevice index"
198 ret = _comedilib_h.comedi_get_write_subdevice(self.device)
200 _error.raise_error(function_name='comedi_get_write_subdevice',
204 def get_write_subdevice(self, **kwargs):
205 "Find streaming output subdevice"
206 return self.subdevice(self._get_write_subdevice(), **kwargs)
208 def _find_subdevice_by_type(self, subdevice_type):
209 "Search for a subdevice index for type `subdevice_type`)."
210 ret = _comedilib_h.comedi_find_subdevice_by_type(
211 self.device, subdevice_type.value, 0) # 0 is starting subdevice
213 _error.raise_error(function_name='comedi_find_subdevice_by_type',
217 def find_subdevice_by_type(self, subdevice_type, **kwargs):
218 """Search for a subdevice by type `subdevice_type`)."
220 `subdevice_type` should be an item from `constant.SUBDEVICE_TYPE`.
222 return self.subdevice(
223 self._find_subdevice_by_type(subdevice_type), **kwargs)
225 cpdef do_insnlist(self, insnlist):
226 """Perform multiple instructions
228 Returns the number of successfully completed instructions.
230 cdef _comedi_h.comedi_insnlist il
232 il.n_insns = len(insnlist)
235 il.insns = <_comedi_h.comedi_insn *>_stdlib.malloc(
236 il.n_insns*sizeof(_comedi_h.comedi_insn))
238 raise _PyComediError('out of memory?')
240 for j,insn in enumerate(insnlist):
242 # By copying the pointer to data, changes to this
243 # copied instruction will also affect the original
244 # instruction's data.
245 il.insns[j] = i.get_comedi_insn()
246 ret = _comedilib_h.comedi_do_insnlist(self.device, &il)
248 _stdlib.free(il.insns)
249 if ret < len(insnlist):
250 _error.raise_error(function_name='comedi_do_insnlist', ret=ret)
253 cpdef do_insn(self, _Insn insn):
254 """Preform a single instruction.
256 Returns an instruction-specific integer.
258 cdef _comedi_h.comedi_insn i
259 # By copying the pointer to data, changes to this
260 # copied instruction will also affect the original
261 # instruction's data.
262 i = insn.get_comedi_insn()
263 ret = _comedilib_h.comedi_do_insn(
266 _error.raise_error(function_name='comedi_do_insn', ret=ret)
269 def get_default_calibration_path(self):
270 "The default calibration path for this device"
271 assert self.device != NULL, (
272 'must call get_default_calibration_path on an open device.')
273 ret = _comedilib_h.comedi_get_default_calibration_path(self.device)
276 function_name='comedi_get_default_calibration_path')
279 def parse_calibration(self, path=None):
280 """The soft calibration from a file for this device.
282 If path is None, the default calibration file is used.
285 path = self.get_default_calibration_path()
286 c = _Calibration(device=self)
290 # extensions to make a more idomatic Python interface
295 def subdevices(self, **kwargs):
296 "Iterate through all available subdevices."
298 for i in range(self.get_n_subdevices()):
299 #yield self.subdevice(i, **kwargs)
300 # Generators are not supported in Cython 0.14.1
301 ret.append(self.subdevice(i, **kwargs))
304 def subdevice(self, index, factory=_Subdevice, **kwargs):
305 return factory(device=self, index=index, **kwargs)