1 # Copyright (C) 2011-2012 W. Trevor King <wking@tremily.us>
2 # Éric Piel <piel@delmic.com>
4 # This file is part of pycomedi.
6 # pycomedi is free software: you can redistribute it and/or modify it under the
7 # terms of the GNU General Public License as published by the Free Software
8 # Foundation, either version 2 of the License, or (at your option) any later
11 # pycomedi is distributed in the hope that it will be useful, but WITHOUT ANY
12 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License along with
16 # pycomedi. If not, see <http://www.gnu.org/licenses/>.
18 "Wrap device-wide Comedi functions in a `Device` class"
21 cimport libc.stdlib as _stdlib
23 from pycomedi import LOG as _LOG
24 from pycomedi import PyComediError as _PyComediError
28 from calibration import Calibration as _Calibration
29 from instruction cimport Insn as _Insn
30 from instruction import Insn as _Insn
31 from subdevice import Subdevice as _Subdevice
34 cdef class Device (object):
37 >>> from . import constant
39 >>> d = Device('/dev/comediX')
43 > d.open() # TODO: re-enable when there is a way to clear comedi_errno
44 Traceback (most recent call last):
46 PyComediError: comedi_open (/dev/comediX): No such file or directory (None)
47 >>> d.filename = '/dev/comedi0'
51 >>> d.get_n_subdevices()
55 >>> d.get_driver_name()
57 >>> s = d.get_read_subdevice()
60 >>> s = d.get_write_subdevice()
63 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.calib)
66 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.pwm)
67 Traceback (most recent call last):
69 PyComediError: comedi_find_subdevice_by_type: Success (-1)
71 As a test instruction, we'll get the time of day, which fills in
72 the data field with `[seconds, microseconds]`.
75 >>> insn.insn = constant.INSN.gtod
76 >>> insn.data = [0, 0] # space where the time value will be stored
81 chanspec: <ChanSpec chan:0 range:0 aref:ground flags:->
84 >>> print insn.data # doctest: +SKIP
86 >>> insn.data = [0, 0]
87 >>> d.do_insnlist([insn])
89 >>> print insn.data # doctest: +SKIP
92 >>> d.get_default_calibration_path()
93 '/var/lib/comedi/calibrations/ni_pcimio_pci-6052e_comedi0'
95 >>> list(d.subdevices()) # doctest: +ELLIPSIS
96 [<pycomedi.subdevice.Subdevice object at 0x...>,...]
105 def __init__(self, filename):
106 super(Device, self).__init__()
107 self.filename = filename
111 self.device = _comedilib_h.comedi_open(self.filename)
112 if self.device == NULL:
113 _error.raise_error(function_name='comedi_open',
114 error_msg=self.filename)
115 self.file = _os.fdopen(self.fileno(), 'r+')
121 ret = _comedilib_h.comedi_close(self.device)
123 _error.raise_error(function_name='comedi_close', ret=ret)
128 "File descriptor for this device"
129 ret = _comedilib_h.comedi_fileno(self.device)
131 _error.raise_error(function_name='comedi_fileno', ret=ret)
134 def get_n_subdevices(self):
135 "Number of subdevices"
136 ret = _comedilib_h.comedi_get_n_subdevices(self.device)
138 _error.raise_error(function_name='comedi_get_n_subdevices',
142 def get_version_code(self):
143 """Comedi version code as a single integer.
145 This is a kernel-module level property, but a valid device is
146 necessary to communicate with the kernel module.
148 version = _comedilib_h.comedi_get_version_code(self.device)
150 _error.raise_error(function_name='comedi_get_version_code',
154 def get_version(self):
155 """Comedi version as a tuple of version numbers.
157 Returns the result of `.get_version_code()`, but rephrased as
158 a tuple of version numbers, e.g. `(0, 7, 61)`.
160 version = self.get_version_code()
163 ret.insert(0, version & 0xff) # grab lowest 8 bits
164 version >>= 8 # shift over 8 bits
167 def get_driver_name(self):
169 ret = _comedilib_h.comedi_get_driver_name(self.device)
171 _error.raise_error(function_name='comedi_get_driver_name',
175 def get_board_name(self):
177 ret = _comedilib_h.comedi_get_board_name(self.device)
179 _error.raise_error(function_name='comedi_get_driver_name',
183 def _get_read_subdevice(self):
184 "Find streaming input subdevice index"
185 ret = _comedilib_h.comedi_get_read_subdevice(self.device)
187 _error.raise_error(function_name='comedi_get_read_subdevice',
191 def get_read_subdevice(self, **kwargs):
192 "Find streaming input subdevice"
193 return self.subdevice(self._get_read_subdevice(), **kwargs)
195 def _get_write_subdevice(self):
196 "Find streaming output subdevice index"
197 ret = _comedilib_h.comedi_get_write_subdevice(self.device)
199 _error.raise_error(function_name='comedi_get_write_subdevice',
203 def get_write_subdevice(self, **kwargs):
204 "Find streaming output subdevice"
205 return self.subdevice(self._get_write_subdevice(), **kwargs)
207 def _find_subdevice_by_type(self, subdevice_type):
208 "Search for a subdevice index for type `subdevice_type`)."
209 ret = _comedilib_h.comedi_find_subdevice_by_type(
210 self.device, subdevice_type.value, 0) # 0 is starting subdevice
212 _error.raise_error(function_name='comedi_find_subdevice_by_type',
216 def find_subdevice_by_type(self, subdevice_type, **kwargs):
217 """Search for a subdevice by type `subdevice_type`)."
219 `subdevice_type` should be an item from `constant.SUBDEVICE_TYPE`.
221 return self.subdevice(
222 self._find_subdevice_by_type(subdevice_type), **kwargs)
224 cpdef do_insnlist(self, insnlist):
225 """Perform multiple instructions
227 Returns the number of successfully completed instructions.
229 cdef _comedi_h.comedi_insnlist il
231 il.n_insns = len(insnlist)
234 il.insns = <_comedi_h.comedi_insn *>_stdlib.malloc(
235 il.n_insns*sizeof(_comedi_h.comedi_insn))
237 raise _PyComediError('out of memory?')
239 for j,insn in enumerate(insnlist):
241 # By copying the pointer to data, changes to this
242 # copied instruction will also affect the original
243 # instruction's data.
244 il.insns[j] = i.get_comedi_insn()
245 ret = _comedilib_h.comedi_do_insnlist(self.device, &il)
247 _stdlib.free(il.insns)
248 if ret < len(insnlist):
249 _error.raise_error(function_name='comedi_do_insnlist', ret=ret)
252 cpdef do_insn(self, _Insn insn):
253 """Preform a single instruction.
255 Returns an instruction-specific integer.
257 cdef _comedi_h.comedi_insn i
258 # By copying the pointer to data, changes to this
259 # copied instruction will also affect the original
260 # instruction's data.
261 i = insn.get_comedi_insn()
262 ret = _comedilib_h.comedi_do_insn(
265 _error.raise_error(function_name='comedi_do_insn', ret=ret)
268 def get_default_calibration_path(self):
269 "The default calibration path for this device"
270 assert self.device != NULL, (
271 'must call get_default_calibration_path on an open device.')
272 ret = _comedilib_h.comedi_get_default_calibration_path(self.device)
275 function_name='comedi_get_default_calibration_path')
278 def parse_calibration(self, path=None):
279 """The soft calibration from a file for this device.
281 If path is None, the default calibration file is used.
284 path = self.get_default_calibration_path()
285 c = _Calibration(device=self)
289 # extensions to make a more idomatic Python interface
294 def subdevices(self, **kwargs):
295 "Iterate through all available subdevices."
297 for i in range(self.get_n_subdevices()):
298 #yield self.subdevice(i, **kwargs)
299 # Generators are not supported in Cython 0.14.1
300 ret.append(self.subdevice(i, **kwargs))
303 def subdevice(self, index, factory=_Subdevice, **kwargs):
304 return factory(device=self, index=index, **kwargs)