pearu / pylibnidaqmx

a Python wrapper to libnidaqmx library
Other
10 stars 9 forks source link

Cannot find library nidaqmx.dll when using nidaqmx 9.0 driver #1

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Install nidaqmx 9.0 from NI website
2. Run setup script included with pylibnidaqmx
3. Try to run: from libnidaqmx import AnalogInputTask

What is the expected output? What do you see instead?
Expected output: none
What I see:
>>> from libnidaqmx import AnalogInputTask
Error: Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Python26\Lib\site-packages\nidaqmx\libnidaqmx.py", line 104, in
<module>
    raise ImportError('Failed to find NI-DAQmx library. Make sure that
libnidaqmx is installed and its location is listed in PATH|LD_LIBRARY_PATH|..')
ImportError: Failed to find NI-DAQmx library. Make sure that libnidaqmx is
installed and its location is listed in PATH|LD_LIBRARY_PATH|..

What version of the product are you using? On what operating system?
pylibnidaqmx version: 12
OS: Windows XP
Python 2.6 - Python(x,y) distribution

Please provide any additional information below.

Searching for any file named nidaqmx.dll on the system shows there is none.
 There are nidaqmx.h and nidaqmx.lib, but no .dll file.  

Searching the NI help forums, I found a message from a delphi developer
searching for the same .dll file
(http://forums.ni.com/ni/board/message?board.id=70&thread.id=1746) .  He
was pointed to a different .dll file: nicaiu.dll.  When I change the
ctypes.util.find_library() call in libnidaqmx.py to search for 'nicau'
instead of 'nidaqmx', it loads the library but generates a new error: 

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Python26\Lib\site-packages\nidaqmx\libnidaqmx.py", line 129, in
<module>
    nidaqmx_version = get_nidaqmx_version()
  File "C:\Python26\Lib\site-packages\nidaqmx\libnidaqmx.py", line 123, in
get_nidaqmx_version
    libnidaqmx.DAQmxGetSysNIDAQMajorVersion(ctypes.byref(d))
ValueError: Procedure called with not enough arguments (4 bytes missing) or
wrong calling convention

Original issue reported on code.google.com by mattl...@gmail.com on 2 Dec 2009 at 12:11

GoogleCodeExporter commented 9 years ago
I wound up writing my own ctypes-based class for accessing the digital I/O 
ports on
my NI-DAQ 6008.  I based it on the examples given in
http://www.scipy.org/Cookbook/Data_Acquisition_with_NIDAQmx and the C code 
provided
with the NIDAQmx 9.0.2 driver.  As seen in the example python code at 
scipy.org, it
uses nicaiu.dll to access the hardware.  The code was tested and it seems to 
work for
what I'm trying to do:

"""
This is an interpretation of the example program
C:\Documents and Settings\All Users\Documents\National
Instruments\NI-DAQ\Examples\DAQmx ANSI C\Digital\Generate Values\Write Dig
Chan\WriteDigChan.c
This routine allows you to access and set the digital output ports.
This module depends on:
numpy
Adapted by Martin Bures [ mbures { @ } zoll { . } com ]
Re-adapted by Matthew Last [ mlast { @ } trexenterprises { . } com]
"""
# import system libraries
import ctypes
import numpy as np

# load DLL
nidaq = ctypes.windll.nicaiu # load the DLL
##############################
# Setup some typedefs and constants
# to correspond with values in
# C:\Program Files\National Instruments\NI-DAQ\DAQmx ANSI C 
Dev\include\NIDAQmx.h
# the typedefs

uint8 = ctypes.c_uint8
uInt32 = ctypes.c_ulong
uInt64 = ctypes.c_ulonglong
float64 = ctypes.c_double
TaskHandle = uInt32
# the constants
DAQmx_Val_ChanForAllLines = 1
DAQmx_Val_GroupByChannel = 0

class Ports( threading.Thread ):
    """
    This class performs the necessary initialization of the DAQ hardware and
    initializes the digital lines to low (digital 0)
    """
    def __init__( self ):
        self.port0 = [0,0,0,0,0,0,0,0]
        self.port1 = [0,0,0,0]

        self.taskHandle0 = TaskHandle( 0 )
        self.taskHandle1 = TaskHandle( 0 )

        # setup the DAQ hardware for port0 and port1
        self.CHK(nidaq.DAQmxCreateTask("",
                          ctypes.byref( self.taskHandle0 )))
        self.CHK(nidaq.DAQmxCreateTask("",
                          ctypes.byref( self.taskHandle1 )))

        self.CHK(nidaq.DAQmxCreateDOChan( self.taskHandle0,
                                   "Dev1/port0/line0:7",
                                   "",
                                   DAQmx_Val_ChanForAllLines))
        self.CHK(nidaq.DAQmxCreateDOChan( self.taskHandle1,
                                   "Dev1/port1/line0:3",
                                   "",
                                   DAQmx_Val_ChanForAllLines))

        self.run(self.taskHandle0)
        self.run(self.taskHandle1)

        # Initialize digital ports
        self.update( port0 = self.port0, port1 = self.port1)

    def CHK( self, err ):
        """a simple error checking routine"""
        if err < 0:
            buf_size = 100
            buf = ctypes.create_string_buffer('\000' * buf_size)
            nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
            raise RuntimeError('nidaq call failed with error %d:
%s'%(err,repr(buf.value)))
        if err > 0:
            buf_size = 100
            buf = ctypes.create_string_buffer('\000' * buf_size)
            nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
            raise RuntimeError('nidaq generated warning %d: %s'%(err,repr(buf.value)))

    def run( self, taskHandle ):
        self.CHK(nidaq.DAQmxStartTask( taskHandle ))

    def stop( self ):
        nidaq.DAQmxStopTask( self.taskHandle0 )
        nidaq.DAQmxClearTask( self.taskHandle0 )

    def update(self, port0 = [0,0,0,0,0,0,0,0], port1 = [0,0,0,0]):
        if len(port0) != 8:
            raise ValueError('port0 argument must have 8 values')
        if len(port1) != 4:
            raise ValueError('port1 argument must have 4 values')

        for i in range(len(port0)):
            self.port0data[i] = port0[i]
        for i in range(len(port1)):
            self.port1data[i] = port1[i]
        self.CHK(nidaq.DAQmxWriteDigitalLines( self.taskHandle0,
                              1, 1, float64(10.0),
                              DAQmx_Val_GroupByChannel,
                              self.port0data.ctypes.data,
                              None,
                              None))
        self.CHK(nidaq.DAQmxWriteDigitalLines( self.taskHandle1,
                              1, 1, float64(10.0),
                              DAQmx_Val_GroupByChannel,
                              self.port1data.ctypes.data,
                              None,
                              None))

Original comment by mattl...@gmail.com on 3 Dec 2009 at 1:04

GoogleCodeExporter commented 9 years ago
I lie!  It doesn't work.  I removed some critical pieces during my code cleanup 
just
before I posted.  Here's the really truly tested and working code:

"""
This is an interpretation of the example program
C:\Documents and Settings\All Users\Documents\National
Instruments\NI-DAQ\Examples\DAQmx ANSI C\Digital\Generate Values\Write Dig
Chan\WriteDigChan.c
This routine allows you to access and set the digital output ports.
This module depends on:
numpy
Adapted by Martin Bures [ mbures { @ } zoll { . } com ]
Re-adapted by Matthew Last [ mlast { @ } trexenterprises { . } com]
"""
# import system libraries
import ctypes
import numpy as np

# load DLL
nidaq = ctypes.windll.nicaiu # load the DLL
##############################
# Setup some typedefs and constants
# to correspond with values in
# C:\Program Files\National Instruments\NI-DAQ\DAQmx ANSI C 
Dev\include\NIDAQmx.h
# the typedefs

uint8 = ctypes.c_uint8
uInt32 = ctypes.c_ulong
uInt64 = ctypes.c_ulonglong
float64 = ctypes.c_double
TaskHandle = uInt32
# the constants
DAQmx_Val_ChanForAllLines = 1
DAQmx_Val_GroupByChannel = 0

class Ports( object ):
    """
    This class performs the necessary initialization of the DAQ hardware and
    initializes the digital lines to low (digital 0)
    """
    def __init__( self ):
        self.port0 = [0,0,0,0,0,0,0,0]
        self.port1 = [0,0,0,0]
        self.port0data = np.zeros((8,), np.uint8 )
        self.port1data = np.zeros((4,), np.uint8 )

        self.taskHandle0 = TaskHandle( 0 )
        self.taskHandle1 = TaskHandle( 0 )

        # setup the DAQ hardware for port0 and port1
        self.CHK(nidaq.DAQmxCreateTask("",
                          ctypes.byref( self.taskHandle0 )))
        self.CHK(nidaq.DAQmxCreateTask("",
                          ctypes.byref( self.taskHandle1 )))

        self.CHK(nidaq.DAQmxCreateDOChan( self.taskHandle0,
                                   "Dev1/port0/line0:7",
                                   "",
                                   DAQmx_Val_ChanForAllLines))
        self.CHK(nidaq.DAQmxCreateDOChan( self.taskHandle1,
                                   "Dev1/port1/line0:3",
                                   "",
                                   DAQmx_Val_ChanForAllLines))

        self.run(self.taskHandle0)
        self.run(self.taskHandle1)

        # Initialize digital ports
        self.update( port0 = self.port0, port1 = self.port1)

    def CHK( self, err ):
        """a simple error checking routine"""
        if err < 0:
            buf_size = 100
            buf = ctypes.create_string_buffer('\000' * buf_size)
            nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
            raise RuntimeError('nidaq call failed with error %d:
%s'%(err,repr(buf.value)))
        if err > 0:
            buf_size = 100
            buf = ctypes.create_string_buffer('\000' * buf_size)
            nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
            raise RuntimeError('nidaq generated warning %d: %s'%(err,repr(buf.value)))

    def run( self, taskHandle ):
        self.CHK(nidaq.DAQmxStartTask( taskHandle ))

    def stop( self ):
        nidaq.DAQmxStopTask( self.taskHandle0 )
        nidaq.DAQmxClearTask( self.taskHandle0 )

    def update(self, port0 = [0,0,0,0,0,0,0,0], port1 = [0,0,0,0]):
        if len(port0) != 8:
            raise ValueError('port0 argument must have 8 values')
        if len(port1) != 4:
            raise ValueError('port1 argument must have 4 values')

        for i in range(len(port0)):
            self.port0data[i] = port0[i]
        for i in range(len(port1)):
            self.port1data[i] = port1[i]
        self.CHK(nidaq.DAQmxWriteDigitalLines( self.taskHandle0,
                              1, 1, float64(10.0),
                              DAQmx_Val_GroupByChannel,
                              self.port0data.ctypes.data,
                              None,
                              None))
        self.CHK(nidaq.DAQmxWriteDigitalLines( self.taskHandle1,
                              1, 1, float64(10.0),
                              DAQmx_Val_GroupByChannel,
                              self.port1data.ctypes.data,
                              None,
                              None))

Original comment by mattl...@gmail.com on 3 Dec 2009 at 1:19

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
I'm also trying to get this to work on windows as well as linux. Here's a first 
patch 
for windows work: test for platform and load windows dll if on win32.  

Should probably specify NIDAQmx.h in setup.cfg, but here just change it to 
where it 
is on my system.
105,113c105
< if sys.platform == 'win32':
<     try:
<         libnidaqmx = ctypes.windll.nicaiu # load the DLL
<     except WindowsError as err:
<         print "Trying to load the national instruments DAQmx dll library on 
windows 
but can't find the nicaiu.dll.  Is it on the PATH? Is NI-DAQmx installed?"
<         raise
< else: 
<     # default: assume we are on linux
<     lib = ctypes.util.find_library('nidaqmx')
---
> lib = ctypes.util.find_library('nidaqmx')
115,116c107,108
<     if lib is None:
<         raise ImportError('Failed to find NI-DAQmx library. Make sure that 
libnidaqmx is installed and its location is listed in PATH|LD_LIBRARY_PATH|..')
---
> if lib is None:
>     raise ImportError('Failed to find NI-DAQmx library. Make sure that 
libnidaqmx 
is installed and its location is listed in PATH|LD_LIBRARY_PATH|..')
118c110
<     libnidaqmx = ctypes.cdll.LoadLibrary(lib)
---
> libnidaqmx = ctypes.cdll.LoadLibrary(lib)
151,152c143
<     #include_nidaqmx_h = '/usr/local/include/NIDAQmx.h'
<     include_nidaqmx_h = r'c:/Programs/NationaInstruments/NI-DAQ/DAQmx ANSI C 
Dev/include/NIDAQmx.h'
---
>     include_nidaqmx_h = '/usr/local/include/NIDAQmx.h'

Original comment by chris.le...@gmail.com on 23 Dec 2009 at 8:25

Attachments:

GoogleCodeExporter commented 9 years ago
Sorry for long delay in responding (I must create issues ml..).

I have commited a patch that adds Windows support to pylibnidaqmx.
The patch is derived from comment #4 but is not tested (users may need
to update include_nidaqmx_h string).
Please report any issues.

Original comment by pearu.peterson on 8 Jan 2010 at 8:43

GoogleCodeExporter commented 9 years ago
Thank you Pearu.
I've now done some tests with r27 with windows xp and NI-DAQmx 8.8 and 9.0. I 
believe 
the only change that needs to be done is to point to where my include file is 
kept.
I also need to becareful that I don't try to start a task if auto_start=True is 
specified during a write() as this raises an error.

The on demand DigitalOutTask task works
The buffered write with internal clock timing works with the AnalogOutTask
and I can trigger the AnalogOutTask using an analog trigger. 

The test acquisition script doesn't work on my system, but I haven't looked yet 
into 
why. 

Original comment by chris.le...@gmail.com on 11 Feb 2010 at 1:19

GoogleCodeExporter commented 9 years ago
On above comment I meant to say that it applied to revision 26 not r27

Original comment by chris.le...@gmail.com on 11 Feb 2010 at 1:21

GoogleCodeExporter commented 9 years ago
It seems that your include file

  c:/Programs/NationaInstruments/NI-DAQ/DAQmx ANSI C 
Dev/include/NIDAQmx.h

is in a non-standard place and, in general, its location cannot be
determined automatically. The path

  C:\Program Files\National Instruments\NI-DAQ\DAQmx ANSI C Dev\include\NIDAQmx.h

seems more reasonable as a standard path.
Anyway, now the trunk has all NI-DAQ defines for 9.0.x version in a .py and the
include file is not used anymore.

I am closing this issue as fixed. If you find other problems, please report 
them as
separate issues.

Original comment by pearu.peterson on 12 Feb 2010 at 7:39

GoogleCodeExporter commented 9 years ago
I agree about the include file.  Perhaps I can come up a with a patch to allow 
the 
setting of the include file location in setup.cfg.

Original comment by chris.le...@gmail.com on 12 Feb 2010 at 6:46