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"
19 from pycomedi cimport _comedi_h
20 from pycomedi cimport _comedilib_h
21 from pycomedi cimport command as _command
22 from . import LOG as _LOG
23 from . import _error as _error
24 from . import channel as _channel
25 from . import constant as _constant
26 from . import command as _command
27 from pycomedi.subdevice_holder cimport SubdeviceHolder as _SubdeviceHolder
28 from .subdevice_holder import SubdeviceHolder as _SubdeviceHolder
29 from .utility import _subdevice_dtype, _subdevice_typecode
32 cdef class Subdevice (_SubdeviceHolder):
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)
70 "Type of subdevice (from `SUBDEVICE_TYPE`)"
71 ret = _comedilib_h.comedi_get_subdevice_type(
72 self._device(), self.index)
74 _error.raise_error(function_name='comedi_get_subdevice_type',
76 return _constant.SUBDEVICE_TYPE.index_by_value(ret)
80 ret = _comedilib_h.comedi_get_subdevice_flags(
81 self._device(), self.index)
83 _error.raise_error(function_name='comedi_get_subdevice_flags',
88 "Subdevice flags (an `SDF` `FlagValue`)"
89 return _constant.FlagValue(
90 _constant.SDF, self._get_flags())
92 def get_n_channels(self):
93 "Number of subdevice channels"
94 ret = _comedilib_h.comedi_get_n_channels(
95 self._device(), self.index)
97 _error.raise_error(function_name='comedi_get_n_channels',
101 def range_is_chan_specific(self):
102 ret = _comedilib_h.comedi_range_is_chan_specific(
103 self._device(), self.index)
106 function_name='comedi_range_is_chan_specific', ret=ret)
109 def maxdata_is_chan_specific(self):
110 ret = _comedilib_h.comedi_maxdata_is_chan_specific(
111 self._device(), self.index)
114 function_name='comedi_maxdata_is_chan_specific', ret=ret)
118 "Reserve the subdevice"
119 ret = _comedilib_h.comedi_lock(self._device(), self.index)
121 _error.raise_error(function_name='comedi_lock', ret=ret)
124 "Release the subdevice"
125 ret = _comedilib_h.comedi_unlock(self._device(), self.index)
127 _error.raise_error(function_name='comedi_unlock', ret=ret)
129 cpdef dio_bitfield(self, unsigned int bits=0, write_mask=0, base_channel=0):
130 """Read/write multiple digital channels.
132 `bits` and `write_mask` are bit fields with the least
133 significant bit representing channel `base_channel`.
135 Returns a bit field containing the read value of all input
136 channels and the last written value of all output channels.
138 ret = _comedilib_h.comedi_dio_bitfield2(
139 self._device(), self.index, write_mask, &bits, base_channel)
141 _error.raise_error(function_name='comedi_dio_bitfield2', ret=ret)
144 # extensions to make a more idomatic Python interface
147 insn = self.device.insn()
148 insn.subdev = self.index
151 def channels(self, **kwargs):
152 "Iterate through all available channels."
153 for i in range(self.get_n_channels()):
154 yield self.channel(i, **kwargs)
156 def channel(self, index, factory=_channel.Channel, **kwargs):
157 "`Channel` instance for the `index`\ed channel."
158 return factory(subdevice=self, index=index, **kwargs)
161 "Return the appropriate `numpy.dtype` based on subdevice flags"
162 return _subdevice_dtype(self)
164 def get_typecode(self):
165 "Return the appropriate `array` type based on subdevice flags"
166 return _subdevice_typecode(self)
169 cdef class StreamingSubdevice (Subdevice):
170 """Streaming I/O subdevice
172 >>> from .device import Device
173 >>> from .chanspec import ChanSpec
174 >>> from . import constant
176 >>> d = Device('/dev/comedi0')
179 >>> s = d.get_read_subdevice(factory=StreamingSubdevice)
181 >>> cmd = s.get_cmd_src_mask()
185 start_src: now|ext|int
187 scan_begin_src: timer|ext
189 convert_src: timer|ext
199 >>> cmd = s.get_cmd_generic_timed(chanlist_len=chanlist_len,
200 ... scan_period_ns=1e3)
201 >>> print str(cmd) # doctest: +NORMALIZE_WHITESPACE
206 scan_begin_src: timer
214 chanlist: [<ChanSpec chan:0 range:0 aref:ground flags:->,
215 <ChanSpec chan:0 range:0 aref:ground flags:->,
216 <ChanSpec chan:0 range:0 aref:ground flags:->]
219 >>> cmd.chanlist = [ChanSpec(chan=i, range=0) for i in range(chanlist_len)]
229 self.cmd = _command.Command()
230 self._command_test_errors = [
232 'unsupported *_src trigger', # unsupported trigger bits zeroed
233 'unsupported *_src combo, or multiple triggers',
234 '*_arg out of range', # offending members adjusted to valid values
235 '*_arg required adjustment', # e.g. trigger timing period rounded
237 # e.g. some boards require same range across channels
240 def get_cmd_src_mask(self):
241 """Detect streaming input/output capabilities
243 The command capabilities of the subdevice indicated by the
244 parameters device and subdevice are probed, and the results
245 placed in the command structure *command. The trigger source
246 elements of the command structure are set to be the bitwise-or
247 of the subdevice's supported trigger sources. Other elements
248 in the structure are undefined.
250 cdef _command.Command cmd
251 cmd = _command.Command()
252 ret = _comedilib_h.comedi_get_cmd_src_mask(
253 self._device(), self.index, cmd.get_comedi_cmd_pointer())
255 _error.raise_error(function_name='comedi_get_cmd_src_mask', ret=ret)
258 def get_cmd_generic_timed(self, chanlist_len, scan_period_ns=0):
259 """Detect streaming input/output capabilities
261 The command capabilities of the subdevice indicated by the
262 parameters device and subdevice are probed, and the results
263 placed in the command structure pointed to by the parameter
264 command. The command structure *command is modified to be a
265 valid command that can be used as a parameter to
266 comedi_command (after the command has additionally been
267 assigned a valid chanlist array). The command measures scans
268 consisting of chanlist_len channels at a scan rate that
269 corresponds to a period of scan_period_ns nanoseconds. The
270 rate is adjusted to a rate that the device can handle.
272 Note that the `ChanSpec` instances in `cmd.chanlist` are not
273 initialized to reasonable values.
275 cdef _command.Command cmd
276 cmd = _command.Command()
277 ret = _comedilib_h.comedi_get_cmd_generic_timed(
278 self._device(), self.index, cmd.get_comedi_cmd_pointer(),
279 chanlist_len, int(scan_period_ns))
280 cmd.chanlist = [0 for i in range(chanlist_len)]
282 _error.raise_error(function_name='comedi_get_cmd_generic_timed',
287 "Stop streaming input/output in progress."
288 ret = _comedilib_h.comedi_cancel(self._device(), self.index)
290 _error.raise_error(function_name='comedi_cancel', ret=ret)
293 "Start streaming input/output"
294 ret = _comedilib_h.comedi_command(
295 self._device(), self.cmd.get_comedi_cmd_pointer())
297 _error.raise_error(function_name='comedi_command', ret=ret)
299 def command_test(self):
300 "Test streaming input/output configuration"
301 ret = _comedilib_h.comedi_command_test(
302 self._device(), self.cmd.get_comedi_cmd_pointer())
303 return self._command_test_errors[ret]
306 """Force updating of streaming buffer
308 If supported by the driver, all available samples are copied
309 to the streaming buffer. These samples may be pending in DMA
310 buffers or device FIFOs. If successful, the number of
311 additional bytes available is returned.
313 ret = _comedilib_h.comedi_poll(self._device(), self.index)
315 _error.raise_error(function_name='comedi_poll', ret=ret)
318 def get_buffer_size(self):
319 "Streaming buffer size of subdevice"
320 ret = _comedilib_h.comedi_get_buffer_size(
321 self._device(), self.index)
323 _error.raise_error(function_name='comedi_get_buffer_size', ret=ret)
326 def set_buffer_size(self, size):
327 """Change the size of the streaming buffer
329 Returns the new buffer size in bytes.
331 The buffer size will be set to size bytes, rounded up to a
332 multiple of the virtual memory page size. The virtual memory
333 page size can be determined using `sysconf(_SC_PAGE_SIZE)`.
335 This function does not require special privileges. However, it
336 is limited to a (adjustable) maximum buffer size, which can be
337 changed by a priveliged user calling
338 `.comedi_set_max_buffer_size`, or running the program
341 ret = _comedilib_h.comedi_set_buffer_size(
342 self._device(), self.index, int(size))
344 _error.raise_error(function_name='comedi_set_buffer_size', ret=ret)
347 def get_max_buffer_size(self):
348 "Maximum streaming buffer size of subdevice"
349 ret = _comedilib_h.comedi_get_max_buffer_size(
350 self._device(), self.index)
352 _error.raise_error(function_name='comedi_get_max_buffer_size',
356 def set_max_buffer_size(self, max_size):
357 """Set the maximum streaming buffer size of subdevice
359 Returns the old (max?) buffer size on success.
361 ret = _comedilib_h.comedi_set_max_buffer_size(
362 self._device(), self.index, int(max_size))
364 _error.raise_error(function_name='comedi_set_max_buffer_size',
368 def get_buffer_contents(self):
369 "Number of bytes available on an in-progress command"
370 ret = _comedilib_h.comedi_get_buffer_contents(
371 self._device(), self.index)
373 _error.raise_error(function_name='comedi_get_buffer_contents',
377 def mark_buffer_read(self, num_bytes):
378 """Next `num_bytes` bytes in the buffer are no longer needed
380 Returns the number of bytes successfully marked as read.
382 This method should only be used if you are using a `mmap()` to
383 read data from Comedi's buffer (as opposed to calling `read()`
384 on the device file), since Comedi will automatically keep
385 track of how many bytes have been transferred via `read()`
388 ret = _comedilib_h.comedi_mark_buffer_read(
389 self._device(), self.index, num_bytes)
391 _error.raise_error(function_name='comedi_mark_buffer_read',
395 def mark_buffer_written(self, num_bytes):
396 """Next `num_bytes` bytes in the buffer are no longer needed
398 Returns the number of bytes successfully marked as written.
400 This method should only be used if you are using a `mmap()` to
401 read data from Comedi's buffer (as opposed to calling
402 `write()` on the device file), since Comedi will automatically
403 keep track of how many bytes have been transferred via
406 ret = _comedilib_h.comedi_mark_buffer_written(
407 self._device(), self.index, num_bytes)
409 _error.raise_error(function_name='comedi_mark_buffer_written',
413 def get_buffer_offset(self):
414 """Offset in bytes of the read(/write?) pointer in the streaming buffer
416 This offset is only useful for memory mapped buffers.
418 ret = _comedilib_h.comedi_get_buffer_offset(
419 self._device(), self.index)
421 _error.raise_error(function_name='comedi_get_buffer_offset', ret=ret)