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."
154 for i in range(self.get_n_channels()):
155 #yield self.channel(i, **kwargs)
156 # Generators are not supported in Cython 0.14.1
157 ret.append(self.channel(i, **kwargs))
160 def channel(self, index, factory=_channel.Channel, **kwargs):
161 "`Channel` instance for the `index`\ed channel."
162 return factory(subdevice=self, index=index, **kwargs)
165 "Return the appropriate `numpy.dtype` based on subdevice flags"
166 return _subdevice_dtype(self)
168 def get_typecode(self):
169 "Return the appropriate `array` type based on subdevice flags"
170 return _subdevice_typecode(self)
173 cdef class StreamingSubdevice (Subdevice):
174 """Streaming I/O subdevice
176 >>> from .device import Device
177 >>> from .chanspec import ChanSpec
178 >>> from . import constant
180 >>> d = Device('/dev/comedi0')
183 >>> s = d.get_read_subdevice(factory=StreamingSubdevice)
185 >>> cmd = s.get_cmd_src_mask()
189 start_src: now|ext|int
191 scan_begin_src: timer|ext
193 convert_src: timer|ext
203 >>> cmd = s.get_cmd_generic_timed(chanlist_len=chanlist_len,
204 ... scan_period_ns=1e3)
205 >>> print str(cmd) # doctest: +NORMALIZE_WHITESPACE
210 scan_begin_src: timer
218 chanlist: [<ChanSpec chan:0 range:0 aref:ground flags:->,
219 <ChanSpec chan:0 range:0 aref:ground flags:->,
220 <ChanSpec chan:0 range:0 aref:ground flags:->]
223 >>> cmd.chanlist = [ChanSpec(chan=i, range=0) for i in range(chanlist_len)]
233 self.cmd = _command.Command()
234 self._command_test_errors = [
236 'unsupported *_src trigger', # unsupported trigger bits zeroed
237 'unsupported *_src combo, or multiple triggers',
238 '*_arg out of range', # offending members adjusted to valid values
239 '*_arg required adjustment', # e.g. trigger timing period rounded
241 # e.g. some boards require same range across channels
244 def get_cmd_src_mask(self):
245 """Detect streaming input/output capabilities
247 The command capabilities of the subdevice indicated by the
248 parameters device and subdevice are probed, and the results
249 placed in the command structure *command. The trigger source
250 elements of the command structure are set to be the bitwise-or
251 of the subdevice's supported trigger sources. Other elements
252 in the structure are undefined.
254 cdef _command.Command cmd
255 cmd = _command.Command()
256 ret = _comedilib_h.comedi_get_cmd_src_mask(
257 self._device(), self.index, cmd.get_comedi_cmd_pointer())
259 _error.raise_error(function_name='comedi_get_cmd_src_mask', ret=ret)
262 def get_cmd_generic_timed(self, chanlist_len, scan_period_ns=0):
263 """Detect streaming input/output capabilities
265 The command capabilities of the subdevice indicated by the
266 parameters device and subdevice are probed, and the results
267 placed in the command structure pointed to by the parameter
268 command. The command structure *command is modified to be a
269 valid command that can be used as a parameter to
270 comedi_command (after the command has additionally been
271 assigned a valid chanlist array). The command measures scans
272 consisting of chanlist_len channels at a scan rate that
273 corresponds to a period of scan_period_ns nanoseconds. The
274 rate is adjusted to a rate that the device can handle.
276 Note that the `ChanSpec` instances in `cmd.chanlist` are not
277 initialized to reasonable values.
279 cdef _command.Command cmd
280 cmd = _command.Command()
281 ret = _comedilib_h.comedi_get_cmd_generic_timed(
282 self._device(), self.index, cmd.get_comedi_cmd_pointer(),
283 chanlist_len, int(scan_period_ns))
284 cmd.chanlist = [0 for i in range(chanlist_len)]
286 _error.raise_error(function_name='comedi_get_cmd_generic_timed',
291 "Stop streaming input/output in progress."
292 ret = _comedilib_h.comedi_cancel(self._device(), self.index)
294 _error.raise_error(function_name='comedi_cancel', ret=ret)
297 "Start streaming input/output"
298 ret = _comedilib_h.comedi_command(
299 self._device(), self.cmd.get_comedi_cmd_pointer())
301 _error.raise_error(function_name='comedi_command', ret=ret)
303 def command_test(self):
304 "Test streaming input/output configuration"
305 ret = _comedilib_h.comedi_command_test(
306 self._device(), self.cmd.get_comedi_cmd_pointer())
307 return self._command_test_errors[ret]
310 """Force updating of streaming buffer
312 If supported by the driver, all available samples are copied
313 to the streaming buffer. These samples may be pending in DMA
314 buffers or device FIFOs. If successful, the number of
315 additional bytes available is returned.
317 ret = _comedilib_h.comedi_poll(self._device(), self.index)
319 _error.raise_error(function_name='comedi_poll', ret=ret)
322 def get_buffer_size(self):
323 "Streaming buffer size of subdevice"
324 ret = _comedilib_h.comedi_get_buffer_size(
325 self._device(), self.index)
327 _error.raise_error(function_name='comedi_get_buffer_size', ret=ret)
330 def set_buffer_size(self, size):
331 """Change the size of the streaming buffer
333 Returns the new buffer size in bytes.
335 The buffer size will be set to size bytes, rounded up to a
336 multiple of the virtual memory page size. The virtual memory
337 page size can be determined using `sysconf(_SC_PAGE_SIZE)`.
339 This function does not require special privileges. However, it
340 is limited to a (adjustable) maximum buffer size, which can be
341 changed by a priveliged user calling
342 `.comedi_set_max_buffer_size`, or running the program
345 ret = _comedilib_h.comedi_set_buffer_size(
346 self._device(), self.index, int(size))
348 _error.raise_error(function_name='comedi_set_buffer_size', ret=ret)
351 def get_max_buffer_size(self):
352 "Maximum streaming buffer size of subdevice"
353 ret = _comedilib_h.comedi_get_max_buffer_size(
354 self._device(), self.index)
356 _error.raise_error(function_name='comedi_get_max_buffer_size',
360 def set_max_buffer_size(self, max_size):
361 """Set the maximum streaming buffer size of subdevice
363 Returns the old (max?) buffer size on success.
365 ret = _comedilib_h.comedi_set_max_buffer_size(
366 self._device(), self.index, int(max_size))
368 _error.raise_error(function_name='comedi_set_max_buffer_size',
372 def get_buffer_contents(self):
373 "Number of bytes available on an in-progress command"
374 ret = _comedilib_h.comedi_get_buffer_contents(
375 self._device(), self.index)
377 _error.raise_error(function_name='comedi_get_buffer_contents',
381 def mark_buffer_read(self, num_bytes):
382 """Next `num_bytes` bytes in the buffer are no longer needed
384 Returns the number of bytes successfully marked as read.
386 This method should only be used if you are using a `mmap()` to
387 read data from Comedi's buffer (as opposed to calling `read()`
388 on the device file), since Comedi will automatically keep
389 track of how many bytes have been transferred via `read()`
392 ret = _comedilib_h.comedi_mark_buffer_read(
393 self._device(), self.index, num_bytes)
395 _error.raise_error(function_name='comedi_mark_buffer_read',
399 def mark_buffer_written(self, num_bytes):
400 """Next `num_bytes` bytes in the buffer are no longer needed
402 Returns the number of bytes successfully marked as written.
404 This method should only be used if you are using a `mmap()` to
405 read data from Comedi's buffer (as opposed to calling
406 `write()` on the device file), since Comedi will automatically
407 keep track of how many bytes have been transferred via
410 ret = _comedilib_h.comedi_mark_buffer_written(
411 self._device(), self.index, num_bytes)
413 _error.raise_error(function_name='comedi_mark_buffer_written',
417 def get_buffer_offset(self):
418 """Offset in bytes of the read(/write?) pointer in the streaming buffer
420 This offset is only useful for memory mapped buffers.
422 ret = _comedilib_h.comedi_get_buffer_offset(
423 self._device(), self.index)
425 _error.raise_error(function_name='comedi_get_buffer_offset', ret=ret)