doc: add a section about Python bindings
authorÉric Piel <piel@delmic.com>
Mon, 29 Oct 2012 17:08:09 +0000 (17:08 +0000)
committerIan Abbott <abbotti@mev.co.uk>
Mon, 29 Oct 2012 17:08:09 +0000 (17:08 +0000)
This section goes into a "Language bindings" section where description
for Perl and Ruby could also go.

doc/Makefile.am
doc/bindings.xml [new file with mode: 0644]
doc/reference.xml

index ac79726c56d0aeefd2aaecf206bacb2116f20038..bad00b261ebb3fa2dcdc80de5767143d801a845b 100644 (file)
@@ -3,7 +3,7 @@ XML = calibration_funcref.xml command_funcref.xml dio_funcref.xml \
        deprecated_funcref.xml error_funcref.xml extensions_funcref.xml \
        drivers.xml funcref.xml glossary.xml \
        install.xml intro.xml other.xml reference.xml tutorial.xml \
-       driverwriting.xml comedilib.xml comedilib.ent
+       driverwriting.xml comedilib.xml bindings.xml comedilib.ent
 
 EXTRA_DIST = $(XML) calibration_funcref.txt command_funcref.txt dio_funcref.txt \
        deprecated_funcref.txt error_funcref.txt extensions_funcref.txt \
@@ -14,7 +14,7 @@ EXTRA_DIST = $(XML) calibration_funcref.txt command_funcref.txt dio_funcref.txt
 
 BUILT_SOURCES = calibration_funcref.xml command_funcref.xml dio_funcref.xml \
        deprecated_funcref.xml error_funcref.xml extensions_funcref.xml \
-       funcref.xml drivers.xml
+       funcref.xml drivers.xml bindings.xml
 
 if HAVE_XMLTO
 all_html = $(srcdir)/doc_html
diff --git a/doc/bindings.xml b/doc/bindings.xml
new file mode 100644 (file)
index 0000000..67de562
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+       "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+<!ENTITY % comedilib_entities SYSTEM "comedilib.ent">
+%comedilib_entities;
+]>
+<section id="languagebindings">
+       <title>Language bindings</title>
+       <para>
+    Comedilib is a C library but comes also with bindings for Python, Perl, and Ruby.
+    This enables usage of a DAQ device directly from these higher level languages.
+    </para>
+
+    <section id="pythonbindings">
+               <title>Python bindings</title>
+               <para>
+        Python bindings are automatically generated by SWIG. So always keep in mind that
+        for precise information the <ulink url="http://www.swig.org/Doc2.0/index.html">SWIG documentation</ulink>
+        can come in handy in addition to the &comedi; documentation.
+        </para>
+        <para>
+        The name of the module is called <filename>comedi</filename>. All the C functions, structs, and
+        constants are directly available as is. So you can refer directly to the C
+        documentation for most of the usage. Note that, as in C, the functions either return the
+        successful result or an error code.  Unlike typical Python functions,
+        no exceptions are generated on errors (excepted type-checking
+        performed by SWIG). For example, to open a &comedi; device you can write in
+        Python:
+        <programlisting>
+import comedi
+device = comedi.comedi_open("/dev/comedi0")
+if device is None:
+    errno = comedi.comedi_errno()
+    print "Error (%d) %s" % (errno, comedi.comedi_strerror(errno))
+    return
+        </programlisting>
+        </para>
+        <para>
+        There are a few things to be aware of. The SWIG bindings automatically take care
+        of converting functions with output parameters to function returning multiple
+        values, if the type of the output parameter is simple. For example
+        <function>comedi_data_read</function> takes as argument a
+        <parameter class="function">lsampl_t *data</parameter> which will contain the data
+        read after the function returns. So in C it is used like this:
+        <programlisting><![CDATA[
+lsampl_t data;
+rc = comedi_data_read(device, subdevice, channel, range, aref, &data);
+        ]]></programlisting>
+
+        As <parameter class="function">lsampl_t</parameter> is a 32-bit
+        <parameter class="function">unsigned int</parameter>, in Python, the function is used like this:
+        <programlisting>
+rc, data = comedi.comedi_data_read(device, subdevice, channel, range, aref)
+        </programlisting>
+        </para>
+        <para>
+        SWIG takes care of converting simple types between Python and C, but does not
+        convert more complex types such as arrays or structs. Special Python classes are
+        created for these. Moreover, &comedi; also provides a few macros. These macros
+        are available in Python as functions with similar names, but lowercase: <function>comedi.cr_pack</function>,
+        <function>comedi.cr_range</function>, etc.
+        So to create and modify a command, one can do in Python:
+        <programlisting>
+cmd = comedi.comedi_cmd_struct()
+comedi.comedi_get_cmd_generic_timed(device, subdevice, cmd, nchans, period_ns)
+cmd.stop_src = comedi.TRIG_COUNT
+cmd.stop_arg = nscans
+# create and add the channel list
+clist = comedi.chanlist(nchans)
+for i, channel in range(nchans):
+    clist[i] = comedi.cr_pack(channel, range, comedi.AREF_GROUND)
+cmd.chanlist = clist
+        </programlisting>
+        </para>
+        <para>
+        One unfortunate consequence is that the objects to represent arrays are as low-level
+        as C arrays (i.e., a pointer with an addition). They have no range checking and
+        no iterator. In addition, they have to be <function>cast</function> from the Python object to the
+        C object. So to create an instruction to call the internal trigger, you would
+        write in Python:
+        <programlisting>
+insn = comedi.comedi_insn_struct()
+insn.subdev = subdevice
+insn.insn = comedi.INSN_INTTRIG
+insn.n = 1
+data = comedi.lsampl_array(insn.n)
+data[0] = num
+insn.data = data.cast()
+rc = comedi.comedi_do_insn(device, insn)
+        </programlisting>
+        </para>
+        <para>
+        When you need to convert from a raw SWIG object to a proxy
+        object, the <function>.frompointer</function> of the Python object can be used. Also note that by default
+        when the proxy object is deleted, SWIG frees the memory associated to it. To
+        still be able to use the memory, you need to set
+        <parameter class="function">.thisown</parameter> to <parameter class="function">False</parameter>.
+        </para>
+        <para>
+        Reading (or writing) a large set of data from a &comedi; device is done via a file.
+        In C, this is done by using a file number, but in Python you need a
+        <parameter class="function">File</parameter> object.
+        You can get a <parameter class="function">File</parameter> object via
+        <function>os.fdopen</function>. For example, to read an array of input data
+        from a &comedi; device into a numpy array, one could do:
+        <programlisting>
+fileno = comedi.comedi_fileno(device)
+file = os.fdopen(fileno, 'r+')
+buf = numpy.fromfile(file, dtype=numpy.uint32, count=(nscans * nchans))
+        </programlisting>
+        </para>
+       </section>
+</section>
index b569d40a8b3a53733ae86d3d2633137e161e34f5..b27966ee63b524031ff9dae2ad1a70fb56ce6381 100644 (file)
@@ -759,5 +759,6 @@ raw/physical conversion functions.
                        <xi:include href="extensions_funcref.xml"/>
                        <xi:include href="deprecated_funcref.xml"/>
        </section>
+       <xi:include href="bindings.xml"/>
        <xi:include href="drivers.xml"/>
 </section>