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 . import LOG as _LOG
25 from . import PyComediError as _PyComediError
26 from pycomedi cimport _comedi_h
27 from pycomedi cimport _comedilib_h
29 from . import calibration as _calibration
30 from pycomedi.device_holder cimport DeviceHolder as _DeviceHolder
31 from . import device_holder as _device_holder
32 from pycomedi cimport instruction as _instruction
33 from . import instruction as _instruction
34 from . import subdevice as _subdevice
37 cdef class Device (_DeviceHolder):
40 >>> from . import constant
42 >>> d = Device('/dev/comediX')
46 > d.open() # TODO: re-enable when there is a way to clear comedi_errno
47 Traceback (most recent call last):
49 PyComediError: comedi_open (/dev/comediX): No such file or directory (None)
50 >>> d.filename = '/dev/comedi0'
54 >>> d.get_n_subdevices()
58 >>> d.get_driver_name()
60 >>> s = d.get_read_subdevice()
63 >>> s = d.get_write_subdevice()
66 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.calib)
69 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.pwm)
70 Traceback (most recent call last):
72 PyComediError: comedi_find_subdevice_by_type: Success (-1)
74 As a test instruction, we'll get the time of day, which fills in
75 the data field with `[seconds, microseconds]`.
78 >>> insn.insn = constant.INSN.gtod
79 >>> insn.data = [0, 0] # space where the time value will be stored
84 chanspec: <ChanSpec chan:0 range:0 aref:ground flags:->
87 >>> print insn.data # doctest: +SKIP
89 >>> insn.data = [0, 0]
90 >>> d.do_insnlist([insn])
92 >>> print insn.data # doctest: +SKIP
95 >>> d.get_default_calibration_path()
96 '/var/lib/comedi/calibrations/ni_pcimio_pci-6052e_comedi0'
98 >>> list(d.subdevices()) # doctest: +ELLIPSIS
99 [<pycomedi.subdevice.Subdevice object at 0x...>,...]
107 def __init__(self, filename):
108 super(Device, self).__init__()
109 self.filename = filename
113 self.device = _comedilib_h.comedi_open(self.filename)
114 if self.device == NULL:
115 _error.raise_error(function_name='comedi_open',
116 error_msg=self.filename)
117 self.file = _os.fdopen(self.fileno(), 'r+')
123 ret = _comedilib_h.comedi_close(self.device)
125 _error.raise_error(function_name='comedi_close', ret=ret)
130 "File descriptor for this device"
131 ret = _comedilib_h.comedi_fileno(self.device)
133 _error.raise_error(function_name='comedi_fileno', ret=ret)
136 def get_n_subdevices(self):
137 "Number of subdevices"
138 ret = _comedilib_h.comedi_get_n_subdevices(self.device)
140 _error.raise_error(function_name='comedi_get_n_subdevices',
144 def get_version_code(self):
145 """Comedi version code as a single integer.
147 This is a kernel-module level property, but a valid device is
148 necessary to communicate with the kernel module.
150 version = _comedilib_h.comedi_get_version_code(self.device)
152 _error.raise_error(function_name='comedi_get_version_code',
156 def get_version(self):
157 """Comedi version as a tuple of version numbers.
159 Returns the result of `.get_version_code()`, but rephrased as
160 a tuple of version numbers, e.g. `(0, 7, 61)`.
162 version = self.get_version_code()
165 ret.insert(0, version & 0xff) # grab lowest 8 bits
166 version >>= 8 # shift over 8 bits
169 def get_driver_name(self):
171 ret = _comedilib_h.comedi_get_driver_name(self.device)
173 _error.raise_error(function_name='comedi_get_driver_name',
177 def get_board_name(self):
179 ret = _comedilib_h.comedi_get_board_name(self.device)
181 _error.raise_error(function_name='comedi_get_driver_name',
185 def _get_read_subdevice(self):
186 "Find streaming input subdevice index"
187 ret = _comedilib_h.comedi_get_read_subdevice(self.device)
189 _error.raise_error(function_name='comedi_get_read_subdevice',
193 def get_read_subdevice(self, **kwargs):
194 "Find streaming input subdevice"
195 return self.subdevice(self._get_read_subdevice(), **kwargs)
197 def _get_write_subdevice(self):
198 "Find streaming output subdevice index"
199 ret = _comedilib_h.comedi_get_write_subdevice(self.device)
201 _error.raise_error(function_name='comedi_get_write_subdevice',
205 def get_write_subdevice(self, **kwargs):
206 "Find streaming output subdevice"
207 return self.subdevice(self._get_write_subdevice(), **kwargs)
209 def _find_subdevice_by_type(self, subdevice_type):
210 "Search for a subdevice index for type `subdevice_type`)."
211 ret = _comedilib_h.comedi_find_subdevice_by_type(
212 self.device, subdevice_type.value, 0) # 0 is starting subdevice
214 _error.raise_error(function_name='comedi_find_subdevice_by_type',
218 def find_subdevice_by_type(self, subdevice_type, **kwargs):
219 """Search for a subdevice by type `subdevice_type`)."
221 `subdevice_type` should be an item from `constant.SUBDEVICE_TYPE`.
223 return self.subdevice(
224 self._find_subdevice_by_type(subdevice_type), **kwargs)
226 cpdef do_insnlist(self, insnlist):
227 """Perform multiple instructions
229 Returns the number of successfully completed instructions.
231 cdef _comedi_h.comedi_insnlist il
232 cdef _instruction.Insn i
233 il.n_insns = len(insnlist)
236 il.insns = <_comedi_h.comedi_insn *>_stdlib.malloc(
237 il.n_insns*sizeof(_comedi_h.comedi_insn))
239 raise _PyComediError('out of memory?')
241 for j,insn in enumerate(insnlist):
243 # By copying the pointer to data, changes to this
244 # copied instruction will also affect the original
245 # instruction's data.
246 il.insns[j] = i.get_comedi_insn()
247 ret = _comedilib_h.comedi_do_insnlist(self.device, &il)
249 _stdlib.free(il.insns)
250 if ret < len(insnlist):
251 _error.raise_error(function_name='comedi_do_insnlist', ret=ret)
254 cpdef do_insn(self, _instruction.Insn insn):
255 """Preform a single instruction.
257 Returns an instruction-specific integer.
259 cdef _comedi_h.comedi_insn i
260 # By copying the pointer to data, changes to this
261 # copied instruction will also affect the original
262 # instruction's data.
263 i = insn.get_comedi_insn()
264 ret = _comedilib_h.comedi_do_insn(
267 _error.raise_error(function_name='comedi_do_insn', ret=ret)
270 def get_default_calibration_path(self):
271 "The default calibration path for this device"
272 assert self.device != NULL, (
273 'must call get_default_calibration_path on an open device.')
274 ret = _comedilib_h.comedi_get_default_calibration_path(self.device)
277 function_name='comedi_get_default_calibration_path')
280 def parse_calibration(self, path=None):
281 """The soft calibration from a file for this device.
283 If path is None, the default calibration file is used.
286 path = self.get_default_calibration_path()
287 c = _calibration.Calibration(device=self)
291 # extensions to make a more idomatic Python interface
294 return _instruction.Insn()
296 def subdevices(self, **kwargs):
297 "Iterate through all available subdevices."
298 for i in range(self.get_n_subdevices()):
299 yield self.subdevice(i, **kwargs)
301 def subdevice(self, index, factory=_subdevice.Subdevice, **kwargs):
302 return factory(device=self, index=index, **kwargs)