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 subdevice-wide Comedi functions in `Subdevice` and related classes"
21 cimport device as _device
22 cimport command as _command
23 from pycomedi import LOG as _LOG
25 from channel import Channel as _Channel
26 import chanspec as _chanspec
27 import constant as _constant
28 import command as _command
29 from utility import _subdevice_dtype, _subdevice_typecode
32 cdef class Subdevice (object):
33 """Class bundling subdevice-related functions
35 >>> from .device import Device
36 >>> from . import constant
38 >>> d = Device('/dev/comedi0')
41 >>> s = d.get_read_subdevice()
45 >>> f # doctest: +ELLIPSIS
46 <pycomedi.constant.FlagValue object at 0x...>
48 cmd_read|readable|ground|common|diff|other|dither
49 >>> s.get_n_channels()
51 >>> s.range_is_chan_specific()
53 >>> s.maxdata_is_chan_specific()
58 >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.dio)
72 def __init__(self, device, index):
73 super(Subdevice, self).__init__()
78 "Type of subdevice (from `SUBDEVICE_TYPE`)"
79 ret = _comedilib_h.comedi_get_subdevice_type(
80 self.device.device, self.index)
82 _error.raise_error(function_name='comedi_get_subdevice_type',
84 return _constant.SUBDEVICE_TYPE.index_by_value(ret)
88 ret = _comedilib_h.comedi_get_subdevice_flags(
89 self.device.device, self.index)
91 _error.raise_error(function_name='comedi_get_subdevice_flags',
96 "Subdevice flags (an `SDF` `FlagValue`)"
97 return _constant.FlagValue(
98 _constant.SDF, self._get_flags())
100 def get_n_channels(self):
101 "Number of subdevice channels"
102 ret = _comedilib_h.comedi_get_n_channels(
103 self.device.device, self.index)
105 _error.raise_error(function_name='comedi_get_n_channels',
109 def range_is_chan_specific(self):
110 ret = _comedilib_h.comedi_range_is_chan_specific(
111 self.device.device, self.index)
114 function_name='comedi_range_is_chan_specific', ret=ret)
117 def maxdata_is_chan_specific(self):
118 ret = _comedilib_h.comedi_maxdata_is_chan_specific(
119 self.device.device, self.index)
122 function_name='comedi_maxdata_is_chan_specific', ret=ret)
126 "Reserve the subdevice"
127 ret = _comedilib_h.comedi_lock(self.device.device, self.index)
129 _error.raise_error(function_name='comedi_lock', ret=ret)
132 "Release the subdevice"
133 ret = _comedilib_h.comedi_unlock(self.device.device, self.index)
135 _error.raise_error(function_name='comedi_unlock', ret=ret)
137 cpdef dio_bitfield(self, unsigned int bits=0, write_mask=0, base_channel=0):
138 """Read/write multiple digital channels.
140 `bits` and `write_mask` are bit fields with the least
141 significant bit representing channel `base_channel`.
143 Returns a bit field containing the read value of all input
144 channels and the last written value of all output channels.
146 ret = _comedilib_h.comedi_dio_bitfield2(
147 self.device.device, self.index, write_mask, &bits, base_channel)
149 _error.raise_error(function_name='comedi_dio_bitfield2', ret=ret)
152 # extensions to make a more idomatic Python interface
155 insn = self.device.insn()
156 insn.subdev = self.index
159 def channels(self, **kwargs):
160 "Iterate through all available channels."
162 for i in range(self.get_n_channels()):
163 #yield self.channel(i, **kwargs)
164 # Generators are not supported in Cython 0.14.1
165 ret.append(self.channel(i, **kwargs))
168 def channel(self, index, factory=_Channel, **kwargs):
169 "`Channel` instance for the `index`\ed channel."
170 return factory(subdevice=self, index=index, **kwargs)
173 "Return the appropriate `numpy.dtype` based on subdevice flags"
174 return _subdevice_dtype(self)
176 def get_typecode(self):
177 "Return the appropriate `array` type based on subdevice flags"
178 return _subdevice_typecode(self)
181 cdef class StreamingSubdevice (Subdevice):
182 """Streaming I/O subdevice
184 >>> from .device import Device
185 >>> from .chanspec import ChanSpec
186 >>> from . import constant
188 >>> d = Device('/dev/comedi0')
191 >>> s = d.get_read_subdevice(factory=StreamingSubdevice)
193 >>> cmd = s.get_cmd_src_mask()
197 start_src: now|ext|int
199 scan_begin_src: timer|ext
201 convert_src: timer|ext
211 >>> cmd = s.get_cmd_generic_timed(chanlist_len=chanlist_len,
212 ... scan_period_ns=1e3)
213 >>> print str(cmd) # doctest: +NORMALIZE_WHITESPACE
218 scan_begin_src: timer
226 chanlist: [<ChanSpec chan:0 range:0 aref:ground flags:->,
227 <ChanSpec chan:0 range:0 aref:ground flags:->,
228 <ChanSpec chan:0 range:0 aref:ground flags:->]
231 >>> cmd.chanlist = [ChanSpec(chan=i, range=0) for i in range(chanlist_len)]
241 self.cmd = _command.Command()
242 self._command_test_errors = [
244 'unsupported *_src trigger', # unsupported trigger bits zeroed
245 'unsupported *_src combo, or multiple triggers',
246 '*_arg out of range', # offending members adjusted to valid values
247 '*_arg required adjustment', # e.g. trigger timing period rounded
249 # e.g. some boards require same range across channels
252 def get_cmd_src_mask(self):
253 """Detect streaming input/output capabilities
255 The command capabilities of the subdevice indicated by the
256 parameters device and subdevice are probed, and the results
257 placed in the command structure *command. The trigger source
258 elements of the command structure are set to be the bitwise-or
259 of the subdevice's supported trigger sources. Other elements
260 in the structure are undefined.
262 cdef _command.Command cmd
263 cmd = _command.Command()
264 ret = _comedilib_h.comedi_get_cmd_src_mask(
265 self.device.device, self.index, cmd.get_comedi_cmd_pointer())
267 _error.raise_error(function_name='comedi_get_cmd_src_mask', ret=ret)
270 def get_cmd_generic_timed(self, chanlist_len, scan_period_ns=0):
271 """Detect streaming input/output capabilities
273 The command capabilities of the subdevice indicated by the
274 parameters device and subdevice are probed, and the results
275 placed in the command structure pointed to by the parameter
276 command. The command structure *command is modified to be a
277 valid command that can be used as a parameter to
278 comedi_command (after the command has additionally been
279 assigned a valid chanlist array). The command measures scans
280 consisting of chanlist_len channels at a scan rate that
281 corresponds to a period of scan_period_ns nanoseconds. The
282 rate is adjusted to a rate that the device can handle.
284 Note that the `ChanSpec` instances in `cmd.chanlist` are not
285 initialized to reasonable values.
287 cdef _command.Command cmd
288 cmd = _command.Command()
289 ret = _comedilib_h.comedi_get_cmd_generic_timed(
290 self.device.device, self.index, cmd.get_comedi_cmd_pointer(),
291 chanlist_len, int(scan_period_ns))
292 cmd.chanlist = [0 for i in range(chanlist_len)]
294 _error.raise_error(function_name='comedi_get_cmd_generic_timed',
299 "Stop streaming input/output in progress."
300 ret = _comedilib_h.comedi_cancel(self.device.device, self.index)
302 _error.raise_error(function_name='comedi_cancel', ret=ret)
305 "Start streaming input/output"
306 ret = _comedilib_h.comedi_command(
307 self.device.device, self.cmd.get_comedi_cmd_pointer())
309 _error.raise_error(function_name='comedi_command', ret=ret)
311 def command_test(self):
312 "Test streaming input/output configuration"
313 ret = _comedilib_h.comedi_command_test(
314 self.device.device, self.cmd.get_comedi_cmd_pointer())
315 return self._command_test_errors[ret]
318 """Force updating of streaming buffer
320 If supported by the driver, all available samples are copied
321 to the streaming buffer. These samples may be pending in DMA
322 buffers or device FIFOs. If successful, the number of
323 additional bytes available is returned.
325 ret = _comedilib_h.comedi_poll(self.device.device, self.index)
327 _error.raise_error(function_name='comedi_poll', ret=ret)
330 def get_buffer_size(self):
331 "Streaming buffer size of subdevice"
332 ret = _comedilib_h.comedi_get_buffer_size(
333 self.device.device, self.index)
335 _error.raise_error(function_name='comedi_get_buffer_size', ret=ret)
338 def set_buffer_size(self, size):
339 """Change the size of the streaming buffer
341 Returns the new buffer size in bytes.
343 The buffer size will be set to size bytes, rounded up to a
344 multiple of the virtual memory page size. The virtual memory
345 page size can be determined using `sysconf(_SC_PAGE_SIZE)`.
347 This function does not require special privileges. However, it
348 is limited to a (adjustable) maximum buffer size, which can be
349 changed by a priveliged user calling
350 `.comedi_set_max_buffer_size`, or running the program
353 ret = _comedilib_h.comedi_set_buffer_size(
354 self.device.device, self.index, int(size))
356 _error.raise_error(function_name='comedi_set_buffer_size', ret=ret)
359 def get_max_buffer_size(self):
360 "Maximum streaming buffer size of subdevice"
361 ret = _comedilib_h.comedi_get_max_buffer_size(
362 self.device.device, self.index)
364 _error.raise_error(function_name='comedi_get_max_buffer_size',
368 def set_max_buffer_size(self, max_size):
369 """Set the maximum streaming buffer size of subdevice
371 Returns the old (max?) buffer size on success.
373 ret = _comedilib_h.comedi_set_max_buffer_size(
374 self.device.device, self.index, int(max_size))
376 _error.raise_error(function_name='comedi_set_max_buffer_size',
380 def get_buffer_contents(self):
381 "Number of bytes available on an in-progress command"
382 ret = _comedilib_h.comedi_get_buffer_contents(
383 self.device.device, self.index)
385 _error.raise_error(function_name='comedi_get_buffer_contents',
389 def mark_buffer_read(self, num_bytes):
390 """Next `num_bytes` bytes in the buffer are no longer needed
392 Returns the number of bytes successfully marked as read.
394 This method should only be used if you are using a `mmap()` to
395 read data from Comedi's buffer (as opposed to calling `read()`
396 on the device file), since Comedi will automatically keep
397 track of how many bytes have been transferred via `read()`
400 ret = _comedilib_h.comedi_mark_buffer_read(
401 self.device.device, self.index, num_bytes)
403 _error.raise_error(function_name='comedi_mark_buffer_read',
407 def mark_buffer_written(self, num_bytes):
408 """Next `num_bytes` bytes in the buffer are no longer needed
410 Returns the number of bytes successfully marked as written.
412 This method should only be used if you are using a `mmap()` to
413 read data from Comedi's buffer (as opposed to calling
414 `write()` on the device file), since Comedi will automatically
415 keep track of how many bytes have been transferred via
418 ret = _comedilib_h.comedi_mark_buffer_written(
419 self.device.device, self.index, num_bytes)
421 _error.raise_error(function_name='comedi_mark_buffer_written',
425 def get_buffer_offset(self):
426 """Offset in bytes of the read(/write?) pointer in the streaming buffer
428 This offset is only useful for memory mapped buffers.
430 ret = _comedilib_h.comedi_get_buffer_offset(
431 self.device.device, self.index)
433 _error.raise_error(function_name='comedi_get_buffer_offset', ret=ret)