"""
Multi-Platform Python wrapper that implements functions from the LabJack 
Windows UD Driver, and the Exodriver.

This python wrapper is intended to make working with your LabJack device easy. The functions contained in this module are helper and device agnostic functions. This module provides the base Device class which the U3, U6, and UE9 classes inherit from.

A typical user should start with their device's module, such as u3.py.
"""
# We use the 'with' keyword to manage the thread-safe device lock. It's built-in on 2.6; 2.5 requires an import.
from __future__ import with_statement

import collections
import ctypes
import os
import struct
from decimal import Decimal
import socket
import Modbus
import atexit # For auto-closing devices
import threading # For a thread-safe device lock

LABJACKPYTHON_VERSION = "4-13-2011"

SOCKET_TIMEOUT = 3
LJSOCKET_TIMEOUT = 62
BROADCAST_SOCKET_TIMEOUT = 1
MAX_USB_PACKET_LENGTH = 64

NUMBER_OF_UNIQUE_LABJACK_PRODUCT_IDS = 5

class LabJackException(Exception):
    """Custom Exception meant for dealing specifically with LabJack Exceptions.

    Error codes are either going to be a LabJackUD error code or a -1.  The -1 implies
    a python wrapper specific error.  
    
    WINDOWS ONLY
    If errorString is not specified then errorString is set by errorCode
    """
    def __init__(self, ec = 0, errorString = ''):
        self.errorCode = ec
        self.errorString = errorString

        if not self.errorString:
            try:
                pString = ctypes.create_string_buffer(256)
                staticLib.ErrorToString(ctypes.c_long(self.errorCode), ctypes.byref(pString))
                self.errorString = pString.value
            except:
                self.errorString = str(self.errorCode)
    
    def __str__(self):
          return self.errorString

# Raised when a low-level command raises an error.
class LowlevelErrorException(LabJackException): pass

# Raised when the return value of OpenDevice is null.
class NullHandleException(LabJackException):
    def __init__(self):
        self.errorString = "Couldn't open device. Please check that the device you are trying to open is connected."

def errcheck(ret, func, args):
    """
    Whenever a function is called through ctypes, the return value is passed to
    this function to be checked for errors.
    
    Support for errno didn't come until 2.6, so Python 2.5 people should 
    upgrade.
    """
    if ret == -1:
        try:
            ec = ctypes.get_errno()
            raise LabJackException(ec, "Exodriver returned error number %s" % ec)
        except AttributeError:
            raise LabJackException(-1, "Exodriver returned an error, but LabJackPython is unable to read the error code. Upgrade to Python 2.6 for this functionality.")
    else:
        return ret

def _loadLinuxSo():
    """
    Attempts to load the liblabjackusb.so for Linux.
    """
    try:
        l = ctypes.CDLL("liblabjackusb.so", use_errno=True)
    except TypeError:
        l = ctypes.CDLL("liblabjackusb.so")
    l.LJUSB_Stream.errcheck = errcheck
    l.LJUSB_Read.errcheck = errcheck
    return l 

def _loadMacDylib():
    """
    Attempts to load the liblabjackusb.dylib for Mac OS X.
    """
    try:
        l = ctypes.CDLL("liblabjackusb.dylib", use_errno=True)
    except TypeError:
        l = ctypes.CDLL("liblabjackusb.dylib")
    l.LJUSB_Stream.errcheck = errcheck
    l.LJUSB_Read.errcheck = errcheck
    return l

def _loadLibrary():
    """_loadLibrary()
    Returns a ctypes dll pointer to the library.
    """
    if(os.name == 'posix'):
        try:
            return _loadLinuxSo()
        except OSError, e:
            pass # We may be on Mac.
        except Exception, e:
            raise LabJackException("Could not load the Linux SO for some reason other than it not being installed. Ethernet connectivity only.\n\n    The error was: %s" % e)
        
        try:
            return _loadMacDylib()
        except OSError, e:
            raise LabJackException("Could not load the Exodriver driver. Ethernet connectivity only.\n\nCheck that the Exodriver is installed, and the permissions are set correctly.\nThe error message was: %s" % e)
        except Exception, e:
            raise LabJackException("Could not load the Mac Dylib for some reason other than it not being installed. Ethernet connectivity only.\n\n    The error was: %s" % e)
                    
    if(os.name == 'nt'):
        try:
            return ctypes.windll.LoadLibrary("labjackud")
        except Exception, e:
            raise LabJackException("Could not load labjackud driver. Ethernet connectivity availability only.\n\n    The error was: %s" % e)

try:
    staticLib = _loadLibrary()
except LabJackException, e:
    print "%s: %s" % ( type(e), e )
    staticLib = None
    
# Attempt to load the windows Skymote library.
try:
    skymoteLib = ctypes.windll.LoadLibrary("liblabjackusb")
except:
    skymoteLib = None

class Device(object):
    """Device(handle, localId = None, serialNumber = None, ipAddress = "", type = None)
            
    Creates a simple 0 with the following functions:
    write(writeBuffer) -- Writes a buffer.
    writeRegister(addr, value) -- Writes a value to a modbus register
    read(numBytes) -- Reads until a packet is received.
    readRegister(addr, numReg = None, format = None) -- Reads a modbus register.
    ping() -- Pings the device.  Returns true if communication worked.
    close() -- Closes the device.
    reset() -- Resets the device.
    """
    def __init__(self, handle, localId = None, serialNumber = None, ipAddress = "", devType = None):
        # Not saving the handle as a void* causes many problems on 64-bit machines.
        if isinstance(handle, int):
            self.handle = ctypes.c_void_p(handle)
        else:
            self.handle = handle
        self.localId = localId
        self.serialNumber = serialNumber
        self.ipAddress = ipAddress
        self.devType = devType
        self.debug = False
        self.streamConfiged = False
        self.streamStarted = False
        self.streamPacketOffset = 0
        self._autoCloseSetup = False
        self.modbusPrependZeros = True
        self.deviceLock = threading.Lock()
        self.deviceName = "LabJack"
        

    def _writeToLJSocketHandle(self, writeBuffer, modbus):
        #if modbus is True and self.modbusPrependZeros:
        #        writeBuffer = [ 0, 0 ] + writeBuffer
            
        packFormat = "B" * len(writeBuffer)
        tempString = struct.pack(packFormat, *writeBuffer)
        
        if modbus:
            self.handle.modbusSocket.send(tempString)
        else:
            self.handle.crSocket.send(tempString)
        
        return writeBuffer

    def _writeToUE9TCPHandle(self, writeBuffer, modbus):
        packFormat = "B" * len(writeBuffer)
        tempString = struct.pack(packFormat, *writeBuffer)
        
        if modbus is True:
            self.handle.modbus.send(tempString)
        else:
            self.handle.data.send(tempString)
        
        return writeBuffer

    def _writeToExodriver(self, writeBuffer, modbus):
        if modbus is True and self.modbusPrependZeros:
            writeBuffer = [ 0, 0 ] + writeBuffer
        
        newA = (ctypes.c_byte*len(writeBuffer))(0) 
        for i in range(len(writeBuffer)):
            newA[i] = ctypes.c_byte(writeBuffer[i])
        
        writeBytes = staticLib.LJUSB_Write(self.handle, ctypes.byref(newA), len(writeBuffer))
        
        if(writeBytes != len(writeBuffer)):
            raise LabJackException( "Could only write %s of %s bytes." % (writeBytes, len(writeBuffer) ) )
            
        return writeBuffer
            
    def _writeToUDDriver(self, writeBuffer, modbus):        
        if self.devType == 0x501:
            newA = (ctypes.c_byte*len(writeBuffer))(0) 
            for i in range(len(writeBuffer)):
                newA[i] = ctypes.c_byte(writeBuffer[i])
            
            writeBytes = skymoteLib.LJUSB_IntWrite(self.handle, 1, ctypes.byref(newA), len(writeBuffer))
            
            if(writeBytes != len(writeBuffer)):
                raise LabJackException( "Could only write %s of %s bytes." % (writeBytes, len(writeBuffer) ) )
        else:
            if modbus is True and self.devType == 9:
                dataWords = len(writeBuffer)
                writeBuffer = [0, 0xF8, 0, 0x07, 0, 0] + writeBuffer #modbus low-level function
                if dataWords % 2 != 0:
                    dataWords = (dataWords+1)/2
                    writeBuffer.append(0)
                else:
                    dataWords = dataWords/2
                writeBuffer[2] = dataWords
                setChecksum(writeBuffer)
            elif modbus is True and self.modbusPrependZeros:
                writeBuffer = [ 0, 0 ] + writeBuffer
            
            eGetRaw(self.handle, LJ_ioRAW_OUT, 0, len(writeBuffer), writeBuffer)
        
        return writeBuffer

    def write(self, writeBuffer, modbus = False, checksum = True):
        """write([writeBuffer], modbus = False)
            
        Writes the data contained in writeBuffer to the device.  writeBuffer must be a list of 
        bytes.
        """
        if self.handle is None:
            raise LabJackException("The device handle is None.")

        if checksum:
            setChecksum(writeBuffer)

        if(isinstance(self.handle, LJSocketHandle)):
            wb = self._writeToLJSocketHandle(writeBuffer, modbus)
        elif(isinstance(self.handle, UE9TCPHandle)):
            wb = self._writeToUE9TCPHandle(writeBuffer, modbus)
        else:
            if os.name == 'posix':
                wb = self._writeToExodriver(writeBuffer, modbus)
            elif os.name == 'nt':
                wb = self._writeToUDDriver(writeBuffer, modbus)
        
        if self.debug: print "Sent: ", hexWithoutQuotes(wb)
    
    def read(self, numBytes, stream = False, modbus = False):
        """read(numBytes, stream = False, modbus = False)
            
        Blocking read until a packet is received.
        """
        readBytes = 0
        
        if self.handle is None:
            raise LabJackException("The device handle is None.")
        
        if(isinstance(self.handle, LJSocketHandle)):
            return self._readFromLJSocketHandle(numBytes, modbus, stream)
            
        elif(isinstance(self.handle, UE9TCPHandle)):
            return self._readFromUE9TCPHandle(numBytes, stream, modbus)
        else:
            if(os.name == 'posix'):
                return self._readFromExodriver(numBytes, stream, modbus)
            elif os.name == 'nt':
                return self._readFromUDDriver(numBytes, stream, modbus)
        
    def _readFromLJSocketHandle(self, numBytes, modbus, spont = False):
        """
        Reads from LJSocket. Returns the result as a list.
        """
        if modbus:
            rcvString = self.handle.modbusSocket.recv(numBytes)
        elif spont:
            rcvString = self.handle.spontSocket.recv(numBytes)
        else:
            rcvString = self.handle.crSocket.recv(numBytes)
        readBytes = len(rcvString)
        packFormat = "B" * readBytes
        rcvDataBuff = struct.unpack(packFormat, rcvString)
        return list(rcvDataBuff)
        
    def _readFromUE9TCPHandle(self, numBytes, stream, modbus):
        if stream is True:
            rcvString = self.handle.stream.recv(numBytes)
        else:
            if modbus is True:
                rcvString = self.handle.modbus.recv(numBytes)
            else:
                rcvString = self.handle.data.recv(numBytes)
        readBytes = len(rcvString)
        packFormat = "B" * readBytes
        rcvDataBuff = struct.unpack(packFormat, rcvString)
        return list(rcvDataBuff)
        
    def _readFromExodriver(self, numBytes, stream, modbus):
        newA = (ctypes.c_byte*numBytes)()
        
        if(stream):
            readBytes = staticLib.LJUSB_Stream(self.handle, ctypes.byref(newA), numBytes)
            if readBytes == 0:
                return ''
            # return the byte string in stream mode
            return struct.pack('b' * readBytes, *newA)
        else:
            readBytes = staticLib.LJUSB_Read(self.handle, ctypes.byref(newA), numBytes)
            # return a list of integers in command/response mode
            return [(newA[i] & 0xff) for i in range(readBytes)]
            
    def _readFromUDDriver(self, numBytes, stream, modbus):
        if self.devType == 0x501:
            newA = (ctypes.c_byte*numBytes)()
            readBytes = skymoteLib.LJUSB_IntRead(self.handle, 0x81, ctypes.byref(newA), numBytes)
            return [(newA[i] & 0xff) for i in range(readBytes)]
        else:
            if modbus is True and self.devType == 9:
                tempBuff = [0] * (8 + numBytes + numBytes%2)
                eGetBuff = list()
                eGetBuff = eGetRaw(self.handle, LJ_ioRAW_IN, 0, len(tempBuff), tempBuff)[1]

                #parse the modbus response out (reponse is the Modbus extended low=level function)
                retBuff = list()
                if len(eGetBuff) >= 9 and eGetBuff[1] == 0xF8 and eGetBuff[3] == 0x07:
                    #figuring out the length of the modbus response
                    mbSize = len(eGetBuff) - 8
                    if len(eGetBuff) >= 14:
                        mbSize = min(mbSize, eGetBuff[13] + 6)
                    i = min(mbSize, numBytes)
                    i = max(i, 0)                    
                    retBuff = eGetBuff[8:8+i] #getting the response only
                return retBuff

            tempBuff = [0] * numBytes
            if stream:
                return eGetRaw(self.handle, LJ_ioRAW_IN, 1, numBytes, tempBuff)[1]
            return eGetRaw(self.handle, LJ_ioRAW_IN, 0, numBytes, tempBuff)[1]
    
    def readRegister(self, addr, numReg = None, format = None, unitId = None):
        """ Reads a specific register from the device and returns the value.
        Requires Modbus.py
        
        readHoldingRegister(addr, numReg = None, format = None)
        addr: The address you would like to read
        numReg: Number of consecutive addresses you would like to read
        format: the unpack format of the returned value ( '>f' or '>I')
        
        Modbus is supported for UE9s over USB from Comm Firmware 1.50 and above.
        """
        
        pkt, numBytes = self._buildReadRegisterPacket(addr, numReg, unitId)
        
        response = self._modbusWriteRead(pkt, numBytes)
        
        return self._parseReadRegisterResponse(response, numBytes, addr, format, numReg)
        
    def _buildReadRegisterPacket(self, addr, numReg, unitId):
        """
        self._buildReadRegisterPacket(addr, numReg)

        Builds a raw modbus "Read Register" packet to be written to a device

        returns a tuple:
        ( < Packet as a list >, < number of bytes to read > )
        """
        # Calculates the number of registers for that request, or if numReg is
        # specified, checks that it is a valid number.
        numReg = Modbus.calcNumberOfRegisters(addr, numReg = numReg)
        
        pkt = Modbus.readHoldingRegistersRequest(addr, numReg = numReg, unitId = unitId)
        pkt = [ ord(c) for c in pkt ]
        
        numBytes = 9 + (2 * int(numReg))
        
        return (pkt, numBytes)
    
    def _parseReadRegisterResponse(self, response, numBytes, addr, format, numReg = None):
        """
        self._parseReadRegisterReponse(reponse, numBytes, addr, format)

        Takes a "Read Register" response and converts it to a value

        returns the value
        """
        if len(response) != numBytes:
            raise LabJackException(9001, "Got incorrect number of bytes from device. Expected %s bytes, got %s bytes. The packet recieved was: %s" % (numBytes, len(response),response))

        if isinstance(response, list):
            packFormat = ">" + "B" * numBytes
            response = struct.pack(packFormat, *response)

        if format == None:
            format = Modbus.calcFormat(addr, numReg)

        value = Modbus.readHoldingRegistersResponse(response, payloadFormat=format)

        return value

        
    def writeRegister(self, addr, value, unitId = None):
        """ 
        Writes a value to a register. Returns the value to be written, if successful.
        Requires Modbus.py
        
        writeRegister(self, addr, value)
        addr: The address you want to write to.
        value: The value, or list of values, you want to write.
        
        if you cannot write to that register, a LabJackException is raised.
        Modbus is not supported for UE9's over USB. If you try it, a LabJackException is raised.
        """
        
        pkt, numBytes = self._buildWriteRegisterPacket(addr, value, unitId)
        
        response = self._modbusWriteRead(pkt, numBytes)
        
        return self._parseWriteRegisterResponse(response, pkt, value)
        
    def _buildWriteRegisterPacket(self, addr, value, unitId):
        """
        self._buildWriteRegisterPacket(addr, value)

        Builds a raw modbus "Write Register" packet to be written to a device

        returns a tuple:
        ( < Packet as a list >, < number of bytes to read > )
        """

        if type(value) is list:
            return self._buildWriteMultipleRegisters(addr, value, unitId)

        fmt = Modbus.calcFormat(addr)
        if fmt != '>H':
            return self._buildWriteFloatToRegister(addr, value, unitId, fmt)

        request = Modbus.writeRegisterRequest(addr, value, unitId)
        request = [ ord(c) for c in request ]
        numBytes = 12
        return request, numBytes

    def _buildWriteFloatToRegister(self, addr, value, unitId, fmt = '>f'):
        numReg = 2

        if not isinstance(value, int) and not isinstance(value, float):
            raise TypeError("Value must be a float or int.")

        # Function, Address, Num Regs, Byte count, Data
        payload = struct.pack('>BHHB', 0x10, addr, 0x02, 0x04) + struct.pack(fmt, value)
        
        request = Modbus._buildHeaderBytes(length = len(payload)+1, unitId = unitId)
        request += payload
        request = [ ord(c) for c in request ]
        numBytes = 12

        return (request, numBytes)
        
        
    def _buildWriteMultipleRegisters(self, startAddr, values, unitId = None):
        request = Modbus.writeRegistersRequest(startAddr, values, unitId)
        request = [ ord(c) for c in request ]
        numBytes = 12

        return (request, numBytes)
        
    def _parseWriteRegisterResponse(self, response, request, value):
        response = list(response)

        if request[2] != 0 and request[3] != 0:
            protoID = (request[2] << 8) + request[3]
            raise Modbus.ModbusException("Got an unexpected protocol ID: %s (expected 0). Please make sure that you have the latest firmware. UE9s need a Comm Firmware of 1.50 or greater.\n\nThe packet you received: %s" % (protoID, hexWithoutQuotes(response)))
        

        if request[7] != response[7]:
            raise LabJackException(9002, "Modbus error number %s raised while writing to register. Make sure you're writing to an address that allows writes.\n\nThe packet you received: %s" % (response[8], hexWithoutQuotes(response)))

        return value
        
    
    def setDIOState(IOnum, state):
        value = (int(state) & 0x01)
        self.writeRegister(6000+IOnum, value)
        return True
    
    def _modbusWriteRead(self, request, numBytes):
        with self.deviceLock:
            self.write(request, modbus = True, checksum = False)
            try:
                result = self.read(numBytes, modbus = True)
                if self.debug: print "Response: ", hexWithoutQuotes(result)
                return result
            except LabJackException:
                self.write(request, modbus = True, checksum = False)
                result = self.read(numBytes, modbus = True)
                if self.debug: print "Response: ", hexWithoutQuotes(result)
                return result
    
    def _checkCommandBytes(self, results, commandBytes):
        """
        Checks all the stuff from a command
        """
        size = len(commandBytes)
        if len(results) == 0:
            raise LabJackException("Got a zero length packet.")
        elif results[0] == 0xB8 and results[1] == 0xB8:
            raise LabJackException("Device detected a bad checksum.")
        elif results[1:(size+1)] != commandBytes:
            raise LabJackException("Got incorrect command bytes.\nExpected: %s\nGot: %s\nFull packet: %s" % (hexWithoutQuotes(commandBytes), hexWithoutQuotes(results[1:(size+1)]), hexWithoutQuotes(results)))
        elif not verifyChecksum(results):
            raise LabJackException("Checksum was incorrect.")
        elif results[6] != 0:
            raise LowlevelErrorException(results[6], "\nThe %s returned an error:\n    %s" % (self.deviceName , lowlevelErrorToString(results[6])) )
            
    def _writeRead(self, command, readLen, commandBytes, checkBytes = True, stream=False, checksum = True):
    
        # Acquire the device lock.
        with self.deviceLock:
            self.write(command, checksum = checksum)
            
            result = self.read(readLen, stream=False)
            if self.debug: print "Response: ", hexWithoutQuotes(result)
            if checkBytes:
                self._checkCommandBytes(result, commandBytes)
                        
            return result
    
    
    def ping(self):
        try:
            if self.devType == LJ_dtUE9:
                writeBuffer = [0x70, 0x70]
                self.write(writeBuffer)
                try:
                    self.read(2)
                except LabJackException:
                    self.write(writeBuffer)
                    self.read(2)
                return True
            
            if self.devType == LJ_dtU3:
                writeBuffer = [0, 0xf8, 0x01, 0x2a, 0, 0, 0, 0]
                writeBuffer = setChecksum(writeBuffer)
                self.write(writeBuffer)
                self.read(40)
                return True

            return False
        except Exception, e:
            print e
            return False
        

    def open(self, devType, Ethernet=False, firstFound = True, serial = None, localId = None, devNumber = None, ipAddress = None, handleOnly = False, LJSocket = None):
        """
        Device.open(devType, Ethernet=False, firstFound = True, serial = None, localId = None, devNumber = None, ipAddress = None, handleOnly = False, LJSocket = None)
        
        Open a device of type devType. 
        """
        
        if self.handle is not None:
            raise LabJackException(9000,"Open called on a device with a handle. Please close the device, and try again. Your device is probably already open.\nLook for lines of code that look like this:\nd = u3.U3()\nd.open() # Wrong! Device is already open.")
        
        ct = LJ_ctUSB
        
        if Ethernet:
            ct = LJ_ctETHERNET
        
        if LJSocket is not None:
            ct = LJ_ctLJSOCKET
        
        d = None
        if devNumber:
            d = openLabJack(devType, ct, firstFound = False, devNumber = devNumber, handleOnly = handleOnly, LJSocket = LJSocket)
        elif serial:
            d = openLabJack(devType, ct, firstFound = False, pAddress = serial, handleOnly = handleOnly, LJSocket = LJSocket)
        elif localId:
            d = openLabJack(devType, ct, firstFound = False, pAddress = localId, handleOnly = handleOnly, LJSocket = LJSocket)
        elif ipAddress:
            d = openLabJack(devType, ct, firstFound = False, pAddress = ipAddress, handleOnly = handleOnly, LJSocket = LJSocket)
        elif LJSocket:
            d = openLabJack(devType, ct, handleOnly = handleOnly, LJSocket = LJSocket)
        elif firstFound:
            d = openLabJack(devType, ct, firstFound = True, handleOnly = handleOnly, LJSocket = LJSocket)
        else:
            raise LabJackException("You must use first found, or give a localId, devNumber, or IP Address")
        
        self.handle = d.handle
        
        if not handleOnly:
            self._loadChangedIntoSelf(d)
            
        self._registerAtExitClose()
                    

    def _loadChangedIntoSelf(self, d):
        for key, value in d.changed.items():
            self.__setattr__(key, value)
            
    def _registerAtExitClose(self):
        if not self._autoCloseSetup:
            # Only need to register auto-close once per device.
            atexit.register(self.close)
            self._autoCloseSetup = True

    def close(self):
        """close()
        
        This function is not specifically supported in the LabJackUD driver
        for Windows and so simply calls the UD function Close.  For Mac and unix
        drivers, this function MUST be performed when finished with a device.
        The reason for this close is because there can not be more than one program
        with a given device open at a time.  If a device is not closed before
        the program is finished it may still be held open and unable to be used
        by other programs until properly closed.
        
        For Windows, Linux, and Mac
        """
        if isinstance(self.handle, UE9TCPHandle) or isinstance(self.handle, LJSocketHandle):
            self.handle.close()
        elif os.name == 'posix':
            staticLib.LJUSB_CloseDevice(self.handle)
        elif self.devType == 0x501:
            skymoteLib.LJUSB_CloseDevice(self.handle)
            
        self.handle = None

    def reset(self):
        """Reset the LabJack device.
    
        For Windows, Linux, and Mac
    
        Sample Usage:
    
        >>> u3 = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
        >>> u3.reset()
        
        @type  None
        @param Function takes no arguments
        
        @rtype: None
        @return: Function returns nothing.
            
        @raise LabJackException: 
        """        
        
        if os.name == 'nt':
            staticLib = ctypes.windll.LoadLibrary("labjackud")
            ec = staticLib.ResetLabJack(self.handle)
    
            if ec != 0: raise LabJackException(ec)
        elif os.name == 'posix':
            sndDataBuff = [0] * 4
            
            #Make the reset packet
            sndDataBuff[0] = 0x9B
            sndDataBuff[1] = 0x99
            sndDataBuff[2] = 0x02
            
            try:
                self.write(sndDataBuff)
                rcvDataBuff = self.read(4)
                if(len(rcvDataBuff) != 4):
                    raise LabJackException(0, "Unable to reset labJack 2")
            except Exception, e:
                raise LabJackException(0, "Unable to reset labjack: %s" % str(e))

    def breakupPackets(self, packets, numBytesPerPacket):
        """
        Name: Device.breakupPackets
        Args: packets, a string or list of packets
              numBytesPerPacket, how big each packe is
        Desc: This function will break up a list into smaller chunks and return
              each chunk one at a time.
        
        >>> l = range(15)
        >>> for packet in d.breakupPackets(l, 5):
        ...     print packet
        [ 0, 1, 2, 3, 4 ]
        [ 5, 6, 7, 8, 9 ]
        [ 10, 11, 12, 13, 14]
        
        """
        start, end = 0, numBytesPerPacket
        while end <= len(packets):
            yield packets[start:end]
            start, end = end, end + numBytesPerPacket

    def samplesFromPacket(self, packet):
        """
        Name: Device.samplesFromPacket
        Args: packet, a packet of stream data
        Desc: This function breaks a packet into all the two byte samples it
              contains and returns them one at a time.
        
        >>> packet = range(16) # fake packet with 1 sample in it
        >>> for sample in d.samplesFromPacket(packet):
        ...     print sample
        [ 12, 13 ]
        """
        HEADER_SIZE = 12
        FOOTER_SIZE = 2
        BYTES_PER_PACKET = 2
        l = str(packet)
        l = l[HEADER_SIZE:]
        l = l[:-FOOTER_SIZE]
        while len(l) > 0:
            yield l[:BYTES_PER_PACKET]
            l = l[BYTES_PER_PACKET:]
            
    def streamStart(self):
        """
        Name: Device.streamStart()
        Args: None
        Desc: Starts streaming on the device.
        Note: You must call streamConfig() before calling this function.
        """
        if not self.streamConfiged:
            raise LabJackException("Stream must be configured before it can be started.")
        
        if self.streamStarted: 
            raise LabJackException("Stream already started.")
        
        command = [ 0xA8, 0xA8 ]
        results = self._writeRead(command, 4, [], False, False, False)
        
        if results[2] != 0:
            raise LowlevelErrorException(results[2], "StreamStart returned an error:\n    %s" % lowlevelErrorToString(results[2]) )
        
        self.streamStarted = True
    
    def streamData(self, convert=True):
        """       
        Name: Device.streamData()
        Args: convert, should the packets be converted as they are read.
                       set to False to get much faster speeds, but you will 
                       have to process the results later.
        Desc: Reads stream data from a LabJack device. See our stream example
              to get an idea of how this function should be called. The return
              value of streamData is a dictionary with the following keys:
              * errors: The number of errors in this block.
              * numPackets: The number of USB packets collected to return this
                            block.
              * missed: The number of readings that were missed because of
                        buffer overflow on the LabJack.
              * firstPacket: The PacketCounter value in the first USB packet.
              * result: The raw bytes returned from read(). The only way to get
                        data if called with convert = False.
              * AINi, where i is an entry in the passed in PChannels. If called
                        with convert = True, this is a list of all the readings
                        in this block.
        Note: You must start the stream by calling streamStart() before calling
              this function.
        """
        if not self.streamStarted:
            raise LabJackException("Please start streaming before reading.")

        numBytes = 14 + (self.streamSamplesPerPacket * 2)

        while True:
        
            result = self.read(numBytes * self.packetsPerRequest, stream = True)
            
            if len(result) == 0:
                yield None
                continue

            numPackets = len(result) // numBytes

            errors = 0
            missed = 0
            firstPacket = ord(result[10])
            for i in range(numPackets):
                e = ord(result[11+(i*numBytes)])
                if e != 0:
                    errors += 1
                    if self.debug and e != 60 and e != 59: print e
                    if e == 60:
                        missed += struct.unpack('<I', result[6+(i*numBytes):10+(i*numBytes)] )[0]
            
            returnDict = dict(numPackets = numPackets, result = result, errors = errors, missed = missed, firstPacket = firstPacket )
            
            if convert:
                returnDict.update(self.processStreamData(result, numBytes = numBytes))
            
            yield returnDict
    
    def streamStop(self):
        """
        Name: Device.streamStop()
        Args: None
        Desc: Stops streaming on the device.
        """
        command = [ 0xB0, 0xB0 ]
        results = self._writeRead(command, 4, [], False, False, False)
        
        if results[2] != 0:
            raise LowlevelErrorException(results[2], "StreamStop returned an error:\n    %s" % lowlevelErrorToString(results[2]) )
        
        self.streamStarted = False


    def getName(self):
        """
        Name: Device.getName()
        Args: None
        Desc: Returns the name of a device.
              Always returns a unicode string.
              Works as of the following firmware versions:
              U6 - 1.00
              U3 - 1.22
              UE9 - 2.00
        
        >>> d = u3.U3()
        >>> d.open()
        >>> d.getName()
        u'My LabJack U3'
        """
        name = list(self.readRegister(58000, format='B'*48, numReg = 24))
        
        if name[1] == 3:
            # Old style string
            name = "My %s" % self.deviceName
            if self.debug: print "Old UTF-16 name detected, replacing with %s" % name
            self.setName(name)
            name = name.decode("UTF-8")
        else:
            try:
                end = name.index(0x00)
                name = struct.pack("B"*end, *name[:end]).decode("UTF-8")
            except ValueError:
                name = "My %s" % self.deviceName
                if self.debug: print "Invalid name detected, replacing with %s" % name 
                self.setName(name)
                name = name.decode("UTF-8")
        
        return name
        
    def setName(self, name = "My LabJack U3"):
        """
        Name: Device.setName(name = ""My LabJack U3")
        Args: name, the name you'd like to assign the the U3
        Desc: Writes a new name to the device.
              Names a limited to 30 characters or less.
              Works as of the following firmware versions:
              U6 - 1.00
              U3 - 1.22
              UE9 - 2.00
        
        >>> d = u3.U3()
        >>> d.open()
        >>> d.getName()
        u'My LabJack U3'
        >>> d.setName("Johann")
        >>> d.getName()
        u'Johann'
        """
        strLen = len(name)
        
        if strLen > 47:
            raise LabJackException("The name is too long, must be less than 48 characters.")
        
        newname = name.encode('UTF-8')
        bl = list(struct.unpack("B"*strLen, newname)) + [0x00]
        strLen += 1
        
        if strLen%2 != 0:
            bl = bl + [0x00]
            strLen += 1
        
        bl = struct.unpack(">"+"H"*(strLen/2), struct.pack("B" * strLen, *bl))
        
        self.writeRegister(58000, list(bl))

    name = property(getName, setName)

    def setDefaults(self, SetToFactoryDefaults = False):
        """
        Name: Device.setDefaults(SetToFactoryDefaults = False)
        Args: SetToFactoryDefaults, set to True reset to factory defaults.
        Desc: Executing this function causes the current or last used values
              (or the factory defaults) to be stored in flash as the power-up
              defaults.
        
        >>> myU6 = U6()
        >>> myU6.setDefaults()
        """
        command = [ 0 ] * 8
        
        #command[0] = Checksum8
        command[1] = 0xF8
        command[2] = 0x01
        command[3] = 0x0E
        #command[4] = Checksum16 (LSB)
        #command[5] = Checksum16 (MSB)
        command[6] = 0xBA
        command[7] = 0x26
        
        if SetToFactoryDefaults:
            command[6] = 0x82
            command[7] = 0xC7
        
        self._writeRead(command, 8, [ 0xF8, 0x01, 0x0E ] )
        
    def setToFactoryDefaults(self):
        return self.setDefaults(SetToFactoryDefaults = True)
    
    validDefaultBlocks = range(8)
    def readDefaults(self, BlockNum, ReadCurrent = False):
        """
        Name: Device.readDefaults(BlockNum)
        Args: BlockNum, which block to read. Must be 0-7.
              ReadCurrent, True = read current configuration
        Desc: Reads the power-up defaults from flash.
        
        >>> myU6 = U6()
        >>> myU6.readDefaults(0)
        [ 0, 0, ... , 0]        
        """
        if BlockNum not in self.validDefaultBlocks:
            raise LabJackException("Defaults must be in range 0-7")
        
        byte7 = (int(bool(ReadCurrent)) << 7) + BlockNum
        
        command = [ 0, 0xF8, 0x01, 0x0E, 0, 0, 0, byte7 ]
        
        result = self._writeRead(command, 40, [ 0xF8, 0x11, 0x0E ])
        
        return result[8:]
        
    def readCurrent(self, BlockNum):
        self.readDefaults(BlockNum, ReadCurrent = True)
        
    def loadGenericDevice(self, device):
        """ Take a generic Device object, and loads it into the current object.
            The generic Device is consumed in the process.
        """
        self.handle = device.handle
        
        self._loadChangedIntoSelf(device)
        
        self._registerAtExitClose()
        
        device = None

# --------------------- BEGIN LabJackPython ---------------------------------

def setChecksum(command):
    """Returns a command with checksums places in the proper locations

    For Windows, Mac, and Linux
    
    Sample Usage:
    
    >>> from LabJackPython import *
    >>> command = [0] * 12
    >>> command[1] = 0xf8
    >>> command[2] = 0x03
    >>> command[3] = 0x0b
    >>> command = SetChecksum(command)
    >>> command
    [7, 248, 3, 11, 0, 0, 0, 0, 0, 0, 0, 0]

    @type  command: List
    @param command: The command by which to calculate the checksum

            
    @rtype: List
    @return: A command list with checksums in the proper locations.
    """  
    
    if len(command) < 8:
        raise LabJackException("Command does not contain enough bytes.")
    
    try:        
        a = command[1]
        
        a = (a & 0x78) >> 3
        
        #Check if the command is an extended command
        if a == 15:
            command = setChecksum16(command)
            command = setChecksum8(command, 6)
            return command
        else:
            command = setChecksum8(command, len(command))
            return command
    except LabJackException, e:
        raise e
    except Exception, e:
        raise LabJackException("SetChecksum Exception:" + str(e))



def verifyChecksum(buffer):
    """Verifies the checksum of a given buffer using the traditional U3/UE9 Command Structure.
    """
    
    buff0 = buffer[0]
    buff4 = buffer[4]
    buff5 = buffer[5]

    tempBuffer = setChecksum(buffer)
    
    if (buff0 == tempBuffer[0]) and (buff4 == tempBuffer[4]) \
    and (buff5 == tempBuffer[5]):
        return True

    return False


# 1 = LJ_ctUSB
def listAll(deviceType, connectionType = 1):
    """listAll(deviceType, connectionType) -> [[local ID, Serial Number, IP Address], ...]
    
    Searches for all devices of a given type over a given connection type and returns a list 
    of all devices found.
    
    WORKS on WINDOWS, MAC, UNIX
    """
    
    if connectionType == LJ_ctLJSOCKET:
        ipAddress, port = deviceType.split(":")
        port = int(port)
        
        serverSocket = socket.socket()
        serverSocket.connect((ipAddress, port))
        serverSocket.settimeout(10)
        
        f = serverSocket.makefile(bufsize = 0)
        f.write("scan\r\n")
        
        l = f.readline().strip()
        try:
            status, numLines = l.split(' ')
        except ValueError:
            raise Exception("Got invalid line from server: %s" % l)
            
        if status.lower().startswith('ok'):
            lines = []
            marked = None
            for i in range(int(numLines)):
                l = f.readline().strip()
                dev = parseline(l)
                lines.append(dev)       
            
            f.close()
            serverSocket.close()
            
            #print "Result of scan:"
            #print lines
            return lines
    
    if deviceType == 12:
        if U12DriverPresent():
            u12Driver = ctypes.windll.LoadLibrary("ljackuw")
            
            # Setup all the ctype arrays
            pSerialNumbers = (ctypes.c_long * 127)(0)
            pIDs = (ctypes.c_long * 127)(0)
            pProdID = (ctypes.c_long * 127)(0)
            pPowerList = (ctypes.c_long * 127)(0)
            pCalMatrix = (ctypes.c_long * 2540)(0)
            pNumFound = ctypes.c_long()
            pFcdd = ctypes.c_long(0)
            pHvc = ctypes.c_long(0)
            
            #Output dictionary
            deviceList = {}
            
            ec = u12Driver.ListAll(ctypes.cast(pProdID, ctypes.POINTER(ctypes.c_long)),
                               ctypes.cast(pSerialNumbers, ctypes.POINTER(ctypes.c_long)),
                               ctypes.cast(pIDs, ctypes.POINTER(ctypes.c_long)),
                               ctypes.cast(pPowerList, ctypes.POINTER(ctypes.c_long)),
                               ctypes.cast(pCalMatrix, ctypes.POINTER(ctypes.c_long)),
                               ctypes.byref(pNumFound),
                               ctypes.byref(pFcdd),
                               ctypes.byref(pHvc))

            if ec != 0: raise LabJackException(ec)
            for i in range(pNumFound.value):
                deviceList[pSerialNumbers[i]] = { 'SerialNumber' : pSerialNumbers[i], 'Id' : pIDs[i], 'ProdId' : pProdID[i], 'powerList' : pPowerList[i] }
                
            return deviceList
    
            
        else:
            return {}
        
    
    if(os.name == 'nt'):
        if deviceType == 0x501:
            if skymoteLib is None:
                raise ImportError("Couldn't load liblabjackusb.dll. Please install, and try again.")
            
            num = skymoteLib.LJUSB_GetDevCount(0x501)
            
            deviceList = dict()
            
            for i in range(num):
               try:
                   device = openLabJack(0x501, 1, firstFound = False, pAddress = None, devNumber = i+1)
                   device.close()
                   deviceList[str(device.serialNumber)] = device.__dict__
               except LabJackException:
                   pass
            
            return deviceList
            
            
        pNumFound = ctypes.c_long()
        pSerialNumbers = (ctypes.c_long * 128)()
        pIDs = (ctypes.c_long * 128)()
        pAddresses = (ctypes.c_double * 128)()
        
        #The actual return variables so the user does not have to use ctypes
        serialNumbers = []
        ids = []
        addresses = []
        
        ec = staticLib.ListAll(deviceType, connectionType, 
                              ctypes.byref(pNumFound), 
                              ctypes.cast(pSerialNumbers, ctypes.POINTER(ctypes.c_long)), 
                              ctypes.cast(pIDs, ctypes.POINTER(ctypes.c_long)), 
                              ctypes.cast(pAddresses, ctypes.POINTER(ctypes.c_long)))
        
        if ec != 0 and ec != 1010: raise LabJackException(ec)
        
        deviceList = dict()
    
        for i in xrange(pNumFound.value):
            if pSerialNumbers[i] != 1010:
                deviceValue = dict(localId = pIDs[i], serialNumber = pSerialNumbers[i], ipAddress = DoubleToStringAddress(pAddresses[i]), devType = deviceType)
                deviceList[pSerialNumbers[i]] = deviceValue
    
        return deviceList

    if(os.name == 'posix'):

        if deviceType == LJ_dtUE9:
            return __listAllUE9Unix(connectionType)
    
        if deviceType == LJ_dtU3:
            return __listAllU3Unix()
        
        if deviceType == 6:
            return __listAllU6Unix()
            
        if deviceType == 0x501:
            return __listAllBridgesUnix()

def isHandleValid(handle):
    if(os.name == 'nt'):
        return True
    else:
        return staticLib.LJUSB_IsHandleValid(handle)


def deviceCount(devType = None):
    """Returns the number of devices connected. """
    if(os.name == 'nt'):
        if devType is None:
            numdev = len(listAll(3))
            numdev += len(listAll(9))
            numdev += len(listAll(6))
            if skymoteLib is not None:
                numdev += len(listAll(0x501))
            return numdev
        else:
            return len(listAll(devType))
    else:
        if devType == None:
            numdev = staticLib.LJUSB_GetDevCount(3)
            numdev += staticLib.LJUSB_GetDevCount(9)
            numdev += staticLib.LJUSB_GetDevCount(6)
            numdev += staticLib.LJUSB_GetDevCount(0x501)
            return numdev
        else:
            return staticLib.LJUSB_GetDevCount(devType)


def getDevCounts():
    if os.name == "nt":
        # Right now there is no good way to count all the U12s on a Windows box
        return { 3 : len(listAll(3)), 6 : len(listAll(6)), 9 : len(listAll(9)), 1 : 0, 0x501 : len(listAll(0x501))}
    else:
        devCounts = (ctypes.c_uint*NUMBER_OF_UNIQUE_LABJACK_PRODUCT_IDS)()
        devIds = (ctypes.c_uint*NUMBER_OF_UNIQUE_LABJACK_PRODUCT_IDS)()
        n = ctypes.c_uint(NUMBER_OF_UNIQUE_LABJACK_PRODUCT_IDS)
        r = staticLib.LJUSB_GetDevCounts(ctypes.byref(devCounts), ctypes.byref(devIds), n)
        
        returnDict = dict()
        
        for i in range(NUMBER_OF_UNIQUE_LABJACK_PRODUCT_IDS):
            returnDict[int(devIds[i])] = int(devCounts[i])
        
        return returnDict



def openAllLabJacks():
    if os.name == "nt":
        # Windows doesn't provide a nice way to open all the devices.
        devs = dict()
        devs[3] = listAll(3)
        devs[6] = listAll(6)
        devs[9] = listAll(9)
        devs[0x501] = listAll(0x501)
        
        devices = list()
        for prodId, numConnected in devs.items():
            for i, serial in enumerate(numConnected.keys()):
                d = Device(None, devType = prodId)
                if prodId == 0x501:
                    d.open(prodId, devNumber = i)
                    d = _makeDeviceFromHandle(d.handle, prodId)
                else:
                    d.open(prodId, serial = serial)
                    d = _makeDeviceFromHandle(d.handle, prodId)
                
                devices.append(d)
    else:
        maxHandles = 10
        devHandles = (ctypes.c_void_p*maxHandles)()
        devIds = (ctypes.c_uint*maxHandles)()
        n = ctypes.c_uint(maxHandles)
        numOpened = staticLib.LJUSB_OpenAllDevices(ctypes.byref(devHandles), ctypes.byref(devIds), n)
        
        devices = list()
        
        for i in range(numOpened):
            devices.append(_makeDeviceFromHandle(devHandles[i], int(devIds[i])))
    
    
    return devices



def _openLabJackUsingLJSocket(deviceType, firstFound, pAddress, LJSocket, handleOnly ):
    if LJSocket is not '':
        ip, port = LJSocket.split(":")
        port = int(port)
        handle = LJSocketHandle(ip, port, deviceType, firstFound, pAddress)
    else:
        handle = LJSocketHandle('localhost', 6000, deviceType, firstFound, pAddress)
      
    return handle

def _openLabJackUsingUDDriver(deviceType, connectionType, firstFound, pAddress, devNumber ):
    if devNumber is not None:
        devs = listAll(deviceType)
        pAddress = devs.keys()[(devNumber-1)]
    
    handle = ctypes.c_long()
    pAddress = str(pAddress)
    ec = staticLib.OpenLabJack(deviceType, connectionType, 
                                pAddress, firstFound, ctypes.byref(handle))

    if ec != 0: raise LabJackException(ec)
    devHandle = handle.value
    
    return devHandle
    
def _openLabJackUsingExodriver(deviceType, firstFound, pAddress, devNumber):
    devType = ctypes.c_ulong(deviceType)
    openDev = staticLib.LJUSB_OpenDevice
    openDev.restype = ctypes.c_void_p
    
    if(devNumber != None):
        handle = openDev(devNumber, 0, devType)
        if handle <= 0:
            raise NullHandleException()
        return handle
    elif(firstFound):
        handle = openDev(1, 0, devType)
        if handle <= 0:
            print "handle: %s" % handle 
            raise NullHandleException()
        return handle
    else:      
        numDevices = staticLib.LJUSB_GetDevCount(deviceType)
        
        for i in range(numDevices):
            handle = openDev(i + 1, 0, devType)
            
            try:
                if handle <= 0:
                    raise NullHandleException()
                device = _makeDeviceFromHandle(handle, deviceType)
            except:
                continue
            
            if device.localId == pAddress or device.serialNumber == pAddress or device.ipAddress == pAddress:
                return device
            else:
                device.close()
        
    raise LabJackException(LJE_LABJACK_NOT_FOUND) 

def _openUE9OverEthernet(firstFound, pAddress, devNumber):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    sndDataBuff = [0] * 6
    sndDataBuff[0] = 0x22
    sndDataBuff[1] = 0x78
    sndDataBuff[3] = 0xa9

    outBuff = ""
    for item in sndDataBuff:
        outBuff += chr(item)
    if not pAddress:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        s.settimeout(BROADCAST_SOCKET_TIMEOUT)
        s.sendto(outBuff, ("255.255.255.255", 52362))
    else:
        s.settimeout(SOCKET_TIMEOUT)
        s.sendto(outBuff, (pAddress, 52362))

    try:
        count = 1
        while True:
            rcvDataBuff = s.recv(128)
            rcvDataBuff = [ord(val) for val in rcvDataBuff]
            if verifyChecksum(rcvDataBuff):
                #Parse the packet
                macAddress = rcvDataBuff[28:34]
                macAddress.reverse()

                # The serial number is four bytes:
                # 0x10 and the last three bytes of the MAC address
                serialBytes = chr(0x10)
                for j in macAddress[3:]:
                    serialBytes += chr(j)
                serialNumber = struct.unpack(">I", serialBytes)[0]

                #Parse out the IP address
                ipAddress = ""
                for j in range(13, 9, -1):
                    ipAddress += str(int(rcvDataBuff[j]))
                    ipAddress += "." 
                ipAddress = ipAddress[0:-1]

                #Local ID
                localId = rcvDataBuff[8] & 0xff
                
                # Check if we have found the device we are looking for.
                # pAddress represents either Local ID, Serial Number, or the
                # IP Address. This is so there are no conflicting identifiers.
                if firstFound \
                or devNumber == count \
                or pAddress in [localId, serialNumber, ipAddress]:
                    handle = UE9TCPHandle(ipAddress)
                    return handle
                
                count += 1
            else:
                # Got a bad checksum.
                pass
    except LabJackException, e:
        raise LabJackException(LJE_LABJACK_NOT_FOUND, "%s" % e)
    except:
        raise LabJackException("LJE_LABJACK_NOT_FOUND: Couldn't find the specified LabJack.")

def _openWirelessBridgeOnWindows(firstFound, pAddress, devNumber):
    if skymoteLib is None:
        raise ImportError("Couldn't load liblabjackusb.dll. Please install, and try again.")
        
    devType = ctypes.c_ulong(0x501)
    openDev = skymoteLib.LJUSB_OpenDevice
    openDev.restype = ctypes.c_void_p
    
    if(devNumber != None):
        handle = openDev(devNumber, 0, devType)
        if handle <= 0:
            raise NullHandleException()
        return handle
    elif(firstFound):
        handle = openDev(1, 0, devType)
        if handle <= 0:
            raise NullHandleException()
        return handle
    else:
        raise LabjackException("Bridges don't have identifiers yet.")
        if handleOnly:
            raise LabjackException("Can't use handleOnly with an id.")
               
        numDevices = skymoteLib.LJUSB_GetDevCount(deviceType)
        
        for i in range(numDevices):
            handle = openDev(i + 1, 0, devType)
            
            try:
                if handle <= 0:
                    raise NullHandleException()
                device = _makeDeviceFromHandle(handle, deviceType)
            except:
                continue
            
            if device.localId == pAddress or device.serialNumber == pAddress or device.ipAddress == pAddress:
                return device
            else:
                device.close()
        
    raise LabJackException(LJE_LABJACK_NOT_FOUND) 

#Windows, Linux, and Mac
def openLabJack(deviceType, connectionType, firstFound = True, pAddress = None, devNumber = None, handleOnly = False, LJSocket = None):
    """openLabJack(deviceType, connectionType, firstFound = True, pAddress = 1, LJSocket = None)
    
        Note: On Windows, Ue9 over Ethernet, pAddress MUST be the IP address. 
    """
    rcvDataBuff = []
    handle = None

    if connectionType == LJ_ctLJSOCKET:
        # LJSocket handles work indepenent of OS
        handle = _openLabJackUsingLJSocket(deviceType, firstFound, pAddress, LJSocket, handleOnly )
    elif os.name == 'posix' and connectionType == LJ_ctUSB:
        # Linux/Mac need to work in the low level driver.
        handle = _openLabJackUsingExodriver(deviceType, firstFound, pAddress, devNumber)
        if isinstance( handle, Device ):
            return handle
    elif os.name == 'nt':
        #If windows operating system then use the UD Driver
        if deviceType == 0x501:
            handle = _openWirelessBridgeOnWindows(firstFound, pAddress, devNumber)
            handle = ctypes.c_void_p(handle)
        elif staticLib is not None:
            handle = _openLabJackUsingUDDriver(deviceType, connectionType, firstFound, pAddress, devNumber ) 
    elif connectionType == LJ_ctETHERNET and deviceType == LJ_dtUE9 :
        handle = _openUE9OverEthernet(firstFound, pAddress, devNumber)
            
    if not handleOnly:
        return _makeDeviceFromHandle(handle, deviceType)
    else:
        return Device(handle, devType = deviceType)

def _makeDeviceFromHandle(handle, deviceType):
    """ A helper function to get set all the info about a device from a handle"""
    device = Device(handle, devType = deviceType)
    device.changed = dict()
    
    if(deviceType == LJ_dtUE9):
        sndDataBuff = [0] * 38
        sndDataBuff[0] = 0x89
        sndDataBuff[1] = 0x78
        sndDataBuff[2] = 0x10
        sndDataBuff[3] = 0x01
        
        try:
            device.write(sndDataBuff, checksum = False)
            rcvDataBuff = device.read(38)
        
            # Local ID
            device.localId = rcvDataBuff[8] & 0xff
        
            # MAC Address
            device.macAddress = "%02X:%02X:%02X:%02X:%02X:%02X" % (rcvDataBuff[33], rcvDataBuff[32], rcvDataBuff[31], rcvDataBuff[30], rcvDataBuff[29], rcvDataBuff[28])
        
            # Parse out serial number
            device.serialNumber = struct.unpack("<I", struct.pack("BBBB", rcvDataBuff[28], rcvDataBuff[29], rcvDataBuff[30], 0x10))[0]
        
            #Parse out the IP address
            device.ipAddress = "%s.%s.%s.%s" % (rcvDataBuff[13], rcvDataBuff[12], rcvDataBuff[11], rcvDataBuff[10] )
            
            # Comm FW Version
            device.commFWVersion = "%s.%02d" % (rcvDataBuff[37], rcvDataBuff[36])
            
            device.changed['localId'] = device.localId
            device.changed['macAddress'] = device.macAddress
            device.changed['serialNumber'] = device.serialNumber
            device.changed['ipAddress'] = device.ipAddress
            device.changed['commFWVersion'] = device.commFWVersion
            
            
        except Exception, e:
            device.close()
            raise e  
        
    elif deviceType == LJ_dtU3:
        sndDataBuff = [0] * 26
        sndDataBuff[0] = 0x0b
        sndDataBuff[1] = 0xf8
        sndDataBuff[2] = 0x0a
        sndDataBuff[3] = 0x08
        
        try:
            device.write(sndDataBuff, checksum = False)
            rcvDataBuff = device.read(38) 
        except LabJackException, e:
            device.close()
            raise e
        
        device.localId = rcvDataBuff[21] & 0xff
        serialNumber = struct.pack("<BBBB", *rcvDataBuff[15:19])
        device.serialNumber = struct.unpack('<I', serialNumber)[0]
        device.ipAddress = ""
        device.firmwareVersion = "%d.%02d" % (rcvDataBuff[10], rcvDataBuff[9])
        device.hardwareVersion = "%d.%02d" % (rcvDataBuff[14], rcvDataBuff[13])
        device.versionInfo = rcvDataBuff[37]
        device.deviceName = 'U3'
        if device.versionInfo == 1:
            device.deviceName += 'B'
        elif device.versionInfo == 2:
            device.deviceName += '-LV'
        elif device.versionInfo == 18:
            device.deviceName += '-HV'
        
        device.changed['localId'] = device.localId
        device.changed['serialNumber'] = device.serialNumber
        device.changed['ipAddress'] = device.ipAddress
        device.changed['firmwareVersion'] = device.firmwareVersion
        device.changed['versionInfo'] = device.versionInfo
        device.changed['deviceName'] = device.deviceName
        device.changed['hardwareVersion'] = device.hardwareVersion
        
    elif deviceType == 6:
        command = [ 0 ] * 26
        command[1] = 0xF8
        command[2] = 0x0A
        command[3] = 0x08
        try:
            device.write(command)
            rcvDataBuff = device.read(38)
        except LabJackException, e:
            device.close()
            raise e
        
        device.localId = rcvDataBuff[21] & 0xff
        serialNumber = struct.pack("<BBBB", *rcvDataBuff[15:19])
        device.serialNumber = struct.unpack('<I', serialNumber)[0]
        device.ipAddress = ""
        
        device.firmwareVersion = "%s.%02d" % (rcvDataBuff[10], rcvDataBuff[9])
        device.bootloaderVersion = "%s.%02d" % (rcvDataBuff[12], rcvDataBuff[11]) 
        device.hardwareVersion = "%s.%02d" % (rcvDataBuff[14], rcvDataBuff[13])
        device.versionInfo = rcvDataBuff[37]
        device.deviceName = 'U6'
        if device.versionInfo == 12:
            device.deviceName = 'U6-Pro'
            
        device.changed['localId'] = device.localId
        device.changed['serialNumber'] = device.serialNumber
        device.changed['ipAddress'] = device.ipAddress
        device.changed['firmwareVersion'] = device.firmwareVersion
        device.changed['versionInfo'] = device.versionInfo
        device.changed['deviceName'] = device.deviceName
        device.changed['hardwareVersion'] = device.hardwareVersion
        device.changed['bootloaderVersion'] = device.bootloaderVersion
        
    elif deviceType == 0x501:
        pkt, readlen = device._buildReadRegisterPacket(65104, 4, 0)
        device.modbusPrependZeros = False
        device.write(pkt, modbus = True, checksum = False)
        for i in range(5):
            try:
                serial = None
                response = device.read(64, False, True)
                serial = device._parseReadRegisterResponse(response[:readlen], readlen, 65104, '>Q', numReg = 4)
                break
            except Modbus.ModbusException:
                pass
                
        if serial is None:
            raise LabJackException("Error reading serial number.")
                
        device.serialNumber = serial
        device.localId = 0
        device.deviceName = "SkyMote Bridge"
        device.changed['localId'] = device.localId
        device.changed['deviceName'] = device.deviceName
        device.changed['serialNumber'] = device.serialNumber
        
    return device

def AddRequest(handle, IOType, Channel, Value, x1, UserData):
    """AddRequest(handle, ioType, channel, value, x1, userData)
        
    Windows Only
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        
        v = ctypes.c_double(Value)
        ud = ctypes.c_double(UserData)
        
        ec = staticLib.AddRequest(Handle, IOType, Channel, v, x1, ud)
        if ec != 0: raise LabJackException(ec)
    else:
       raise LabJackException(0, "Function only supported for Windows")


#Windows
def AddRequestS(Handle, pIOType, Channel, Value, x1, UserData):
    """Add a request to the LabJackUD request stack
    
    For Windows
    
    Sample Usage to get the AIN value from channel 0:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequestS(u3Handle,"LJ_ioGET_AIN", 0, 0.0, 0, 0.0)
    >>> Go()
    >>> value = GetResult(u3Handle, LJ_ioGET_AIN, 0)
    >>> print "Value:" + str(value)
    Value:0.366420765873
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  IOType: String
    @param IOType: IO Request to the LabJack.
    @type  Channel: number
    @param Channel: Channel for the IO request.
    @type  Value: number
    @param Value: Used for some requests
    @type  x1: number
    @param x1: Used for some requests
    @type  UserData: number
    @param UserData: Used for some requests
    
    @rtype: None
    @return: Function returns nothing.
    
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        
        v = ctypes.c_double(Value)
        ud = ctypes.c_double(UserData)
        
        ec = staticLib.AddRequestS(Handle, pIOType, Channel, 
                                    v, x1, ud)

        if ec != 0: raise LabJackException(ec)
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def AddRequestSS(Handle, pIOType, pChannel, Value, x1, UserData):
    """Add a request to the LabJackUD request stack
    
    For Windows
    
    Sample Usage to get the AIN value from channel 0:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequestSS(u3Handle,"LJ_ioGET_CONFIG", "LJ_chFIRMWARE_VERSION", 0.0, 0, 0.0)
    >>> Go()
    >>> value = GetResultS(u3Handle, "LJ_ioGET_CONFIG", LJ_chFIRMWARE_VERSION)
    >>> print "Value:" + str(value)
    Value:1.27
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  IOType: String
    @param IOType: IO Request to the LabJack.
    @type  Channel: String
    @param Channel: Channel for the IO request.
    @type  Value: number
    @param Value: Used for some requests
    @type  x1: number
    @param x1: Used for some requests
    @type  UserData: number
    @param UserData: Used for some requests
    
    @rtype: None
    @return: Function returns nothing.
    
    @raise LabJackException:
    """
    if os.name == 'nt':      
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        
        v = ctypes.c_double(Value)
        ud = ctypes.c_double(UserData)
        
        ec = staticLib.AddRequestSS(Handle, pIOType, pChannel, 
                                     v, x1, ud)

        if ec != 0: raise LabJackException(ec)
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def Go():
    """Complete all requests currently on the LabJackUD request stack

    For Windows Only
    
    Sample Usage:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequestSS(u3Handle,"LJ_ioGET_CONFIG", "LJ_chFIRMWARE_VERSION", 0.0, 0, 0.0)
    >>> Go()
    >>> value = GetResultS(u3Handle, "LJ_ioGET_CONFIG", LJ_chFIRMWARE_VERSION)
    >>> print "Value:" + str(value)
    Value:1.27
    
    @rtype: None
    @return: Function returns nothing.
    
    @raise LabJackException:
    """
    
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")           
        ec = staticLib.Go()

        if ec != 0: raise LabJackException(ec)
    else:
       raise LabJackException("Function only supported for Windows")

#Windows
def GoOne(Handle):
    """Performs the next request on the LabJackUD request stack
    
    For Windows Only
    
    Sample Usage:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequestSS(u3Handle,"LJ_ioGET_CONFIG", "LJ_chFIRMWARE_VERSION", 0.0, 0, 0.0)
    >>> GoOne(u3Handle)
    >>> value = GetResultS(u3Handle, "LJ_ioGET_CONFIG", LJ_chFIRMWARE_VERSION)
    >>> print "Value:" + str(value)
    Value:1.27
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    
    @rtype: None
    @return: Function returns nothing.
    
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")           
        ec = staticLib.GoOne(Handle)

        if ec != 0: raise LabJackException(ec)
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def eGet(Handle, IOType, Channel, pValue, x1):
    """Perform one call to the LabJack Device
    
    eGet is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> eGet(u3Handle, LJ_ioGET_AIN, 0, 0, 0)
    0.39392614550888538
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  IOType: number
    @param IOType: IO Request to the LabJack.
    @type  Channel: number
    @param Channel: Channel for the IO request.
    @type  Value: number
    @param Value: Used for some requests
    @type  x1: number
    @param x1: Used for some requests
    
    @rtype: number
    @return: Returns the value requested.
        - value
        
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pv = ctypes.c_double(pValue)
        #ppv = ctypes.pointer(pv)
        ec = staticLib.eGet(Handle, IOType, Channel, ctypes.byref(pv), x1)
        #staticLib.eGet.argtypes = [ctypes.c_long, ctypes.c_long, ctypes.c_long, ctypes.c_double, ctypes.c_long]
        #ec = staticLib.eGet(Handle, IOType, Channel, pValue, x1)
        
        if ec != 0: raise LabJackException(ec)
        #print "EGet:" + str(ppv)
        #print "Other:" + str(ppv.contents)
        return pv.value
    else:
       raise LabJackException(0, "Function only supported for Windows")


#Windows
#Raw method -- Used because x1 is an output
def eGetRaw(Handle, IOType, Channel, pValue, x1):
    """Perform one call to the LabJack Device as a raw command
    
    eGetRaw is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage (Calling a echo command):
    
    >>> sendBuff = [0] * 2
    >>> sendBuff[0] = 0x70
    >>> sendBuff[1] = 0x70
    >>> eGetRaw(ue9Handle, LJ_ioRAW_OUT, 0, len(sendBuff), sendBuff)
    (2.0, [112, 112])
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  IOType: number
    @param IOType: IO Request to the LabJack.
    @type  Channel: number
    @param Channel: Channel for the IO request.
    @type  pValue: number
    @param Value: Length of the buffer.
    @type  x1: number
    @param x1: Buffer to send.
    
    @rtype: Tuple
    @return: The tuple (numBytes, returnBuffer)
        - numBytes (number)
        - returnBuffer (List)
        
    @raise LabJackException:
    """
    ec = 0
    x1Type = "int"
    if os.name == 'nt':
        digitalConst = [35, 36, 37, 45]
        pv = ctypes.c_double(pValue)

        #If IOType is digital then call eget with x1 as a long
        if IOType in digitalConst:
            ec = staticLib.eGet(Handle, IOType, Channel, ctypes.byref(pv), x1)
        else: #Otherwise as an array
            
            try:
                #Verify x1 is an array
                if len(x1) < 1:
                    raise LabJackException(0, "x1 is not a valid variable for the given IOType") 
            except Exception:
                raise LabJackException(0, "x1 is not a valid variable for the given IOType")  
            
            #Initialize newA
            newA = None
            if type(x1[0]) == int:
                newA = (ctypes.c_byte*len(x1))()
                for i in range(0, len(x1), 1):
                    newA[i] = ctypes.c_byte(x1[i])
            else:
                x1Type = "float"
                newA = (ctypes.c_double*len(x1))()
                for i in range(0, len(x1), 1):
                    newA[i] = ctypes.c_double(x1[i])

            ec = staticLib.eGet(Handle, IOType, Channel, ctypes.byref(pv), ctypes.byref(newA))
            
            if IOType == LJ_ioRAW_IN and Channel == 1:
                # We return the raw byte string if we are streaming
                x1 = struct.pack('b' * len(x1), *newA)
            elif IOType == LJ_ioRAW_IN and Channel == 0:
                x1 = [0] * int(pv.value)
                for i in range(len(x1)):
                    x1[i] = newA[i] & 0xff
                
            else:
                x1 = [0] * len(x1)
                for i in range(len(x1)):
                    x1[i] = newA[i]
                    if(x1Type == "int"):
                        x1[i] = x1[i] & 0xff
            
        if ec != 0: raise LabJackException(ec)
        return pv.value, x1
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def eGetS(Handle, pIOType, Channel, pValue, x1):
    """Perform one call to the LabJack Device
    
    eGet is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> eGet(u3Handle, "LJ_ioGET_AIN", 0, 0, 0)
    0.39392614550888538
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  pIOType: String
    @param pIOType: IO Request to the LabJack.
    @type  Channel: number
    @param Channel: Channel for the IO request.
    @type  Value: number
    @param Value: Used for some requests
    @type  x1: number
    @param x1: Used for some requests
    
    @rtype: number
    @return: Returns the value requested.
        - value
        
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pv = ctypes.c_double(pValue)
        ec = staticLib.eGetS(Handle, pIOType, Channel, ctypes.byref(pv), x1)

        if ec != 0: raise LabJackException(ec)
        return pv.value
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def eGetSS(Handle, pIOType, pChannel, pValue, x1):
    """Perform one call to the LabJack Device
    
    eGet is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> eGetSS(u3Handle,"LJ_ioGET_CONFIG", "LJ_chFIRMWARE_VERSION", 0, 0)
    1.27
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  pIOType: String
    @param pIOType: IO Request to the LabJack.
    @type  Channel: String
    @param Channel: Channel for the IO request.
    @type  Value: number
    @param Value: Used for some requests
    @type  x1: number
    @param x1: Used for some requests
    
    @rtype: number
    @return: Returns the value requested.
        - value
        
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pv = ctypes.c_double(pValue)
        ec = staticLib.eGetSS(Handle, pIOType, pChannel, ctypes.byref(pv), x1)

        if ec != 0: raise LabJackException(ec)
        return pv.value
    else:
       raise LabJackException(0, "Function only supported for Windows")


#Windows
#Not currently implemented
def eGetRawS(Handle, pIOType, Channel, pValue, x1):
    """Function not yet implemented.
    
    For Windows only.
    """
    pass

#Windows
def ePut(Handle, IOType, Channel, Value, x1):
    """Put one value to the LabJack device
    
    ePut is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> eGet(u3Handle, LJ_ioGET_CONFIG, LJ_chLOCALID, 0, 0)
    0.0
    >>> ePut(u3Handle, LJ_ioPUT_CONFIG, LJ_chLOCALID, 8, 0)
    >>> eGet(u3Handle, LJ_ioGET_CONFIG, LJ_chLOCALID, 0, 0)
    8.0
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  IOType: number
    @param IOType: IO Request to the LabJack.
    @type  Channel: number
    @param Channel: Channel for the IO request.
    @type  Value: number
    @param Value: Used for some requests
    @type  x1: number
    @param x1: Used for some requests
    
    @rtype: None
    @return: Function returns nothing.
    
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pv = ctypes.c_double(Value)
        ec = staticLib.ePut(Handle, IOType, Channel, pv, x1)

        if ec != 0: raise LabJackException(ec)
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def ePutS(Handle, pIOType, Channel, Value, x1):
    """Put one value to the LabJack device
    
    ePut is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> eGet(u3Handle, LJ_ioGET_CONFIG, LJ_chLOCALID, 0, 0)
    0.0
    >>> ePutS(u3Handle, "LJ_ioPUT_CONFIG", LJ_chLOCALID, 8, 0)
    >>> eGet(u3Handle, LJ_ioGET_CONFIG, LJ_chLOCALID, 0, 0)
    8.0
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  IOType: String
    @param IOType: IO Request to the LabJack.
    @type  Channel: number
    @param Channel: Channel for the IO request.
    @type  Value: number
    @param Value: Used for some requests
    @type  x1: number
    @param x1: Used for some requests
    
    @rtype: None
    @return: Function returns nothing.
    
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        
        pv = ctypes.c_double(Value)
        ec = staticLib.ePutS(Handle, pIOType, Channel, pv, x1)

        if ec != 0: raise LabJackException(ec)
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def ePutSS(Handle, pIOType, pChannel, Value, x1):
    """Put one value to the LabJack device
    
    ePut is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> eGet(u3Handle, LJ_ioGET_CONFIG, LJ_chLOCALID, 0, 0)
    0.0
    >>> ePutSS(u3Handle, "LJ_ioPUT_CONFIG", "LJ_chLOCALID", 8, 0)
    >>> eGet(u3Handle, LJ_ioGET_CONFIG, LJ_chLOCALID, 0, 0)
    8.0
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  IOType: String
    @param IOType: IO Request to the LabJack.
    @type  Channel: String
    @param Channel: Channel for the IO request.
    @type  Value: number
    @param Value: Used for some requests
    @type  x1: number
    @param x1: Used for some requests
    
    @rtype: None
    @return: Function returns nothing.
    
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")

        pv = ctypes.c_double(Value)
        ec = staticLib.ePutSS(Handle, pIOType, pChannel, pv, x1)

        if ec != 0: raise LabJackException(ec)
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def GetResult(Handle, IOType, Channel):
    """Put one value to the LabJack device
    
    ePut is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequestSS(u3Handle,"LJ_ioGET_CONFIG", "LJ_chFIRMWARE_VERSION", 0.0, 0, 0.0)
    >>> GoOne(u3Handle)
    >>> value = GetResult(u3Handle, LJ_ioGET_CONFIG, LJ_chFIRMWARE_VERSION)
    >>> print "Value:" + str(value)
    Value:1.27
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  IOType: number
    @param IOType: IO Request to the LabJack.
    @type  Channel: number
    @param Channel: Channel for the IO request.
    
    @rtype: number
    @return: The value requested.
        - value
        
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pv = ctypes.c_double()
        ec = staticLib.GetResult(Handle, IOType, Channel, ctypes.byref(pv))

        if ec != 0: raise LabJackException(ec)          
        return pv.value
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def GetResultS(Handle, pIOType, Channel):
    """Put one value to the LabJack device
    
    ePut is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequestSS(u3Handle,"LJ_ioGET_CONFIG", "LJ_chFIRMWARE_VERSION", 0.0, 0, 0.0)
    >>> GoOne(u3Handle)
    >>> value = GetResultS(u3Handle, "LJ_ioGET_CONFIG", LJ_chFIRMWARE_VERSION)
    >>> print "Value:" + str(value)
    Value:1.27
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  pIOType: String
    @param pIOType: IO Request to the LabJack.
    @type  Channel: number
    @param Channel: Channel for the IO request.
    
    @rtype: number
    @return: The value requested.
        - value
        
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pv = ctypes.c_double()
        ec = staticLib.GetResultS(Handle, pIOType, Channel, ctypes.byref(pv))

        if ec != 0: raise LabJackException(ec)          
        return pv.value
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def GetResultSS(Handle, pIOType, pChannel):
    """Put one value to the LabJack device
    
    ePut is equivilent to an AddRequest followed by a GoOne.
    
    For Windows Only
    
    Sample Usage:
    
    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequestSS(u3Handle,"LJ_ioGET_CONFIG", "LJ_chFIRMWARE_VERSION", 0.0, 0, 0.0)
    >>> GoOne(u3Handle)
    >>> value = GetResultSS(u3Handle, "LJ_ioGET_CONFIG", "LJ_chFIRMWARE_VERSION")
    >>> print "Value:" + str(value)
    Value:1.27
    
    @type  Handle: number
    @param Handle: Handle to the LabJack device.
    @type  pIOType: String
    @param pIOType: IO Request to the LabJack.
    @type  Channel: String
    @param Channel: Channel for the IO request.
    
    @rtype: number
    @return: The value requested.
        - value
        
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pv = ctypes.c_double()
        ec = staticLib.GetResultS(Handle, pIOType, pChannel, ctypes.byref(pv))

        if ec != 0: raise LabJackException(ec)          
        return pv.value
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def GetFirstResult(Handle):
    """List All LabJack devices of a specific type over a specific connection type.

    For Windows only.

    Sample Usage (Shows getting the localID (8) and firmware version (1.27) of a U3 device):

    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequest(u3Handle, LJ_ioGET_CONFIG, LJ_chLOCALID, 0, 0, 0)
    >>> AddRequest(u3Handle, LJ_ioGET_CONFIG, LJ_chFIRMWARE_VERSION, 0, 0, 0)
    >>> Go()
    >>> GetFirstResult(u3Handle)
    (1001, 0, 8.0, 0, 0.0)
    >>> GetNextResult(u3Handle)
    (1001, 11, 1.27, 0, 0.0)

    @type  DeviceType: number
    @param DeviceType: The LabJack device.
    @type  ConnectionType: number
    @param ConnectionType: The connection method (Ethernet/USB).
    
    @rtype: Tuple
    @return: The tuple (ioType, channel, value, x1, userData)
        - ioType (number): The io of the result.
        - serialNumber (number): The channel of the result.
        - value (number): The requested result.
        - x1 (number):  Used only in certain requests.
        - userData (number): Used only in certain requests.
        
    @raise LabJackException: 
    """   
    if os.name == 'nt':     
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pio = ctypes.c_long()
        pchan = ctypes.c_long()
        pv = ctypes.c_double()
        px = ctypes.c_long()
        pud = ctypes.c_double()
        ec = staticLib.GetFirstResult(Handle, ctypes.byref(pio), 
                                       ctypes.byref(pchan), ctypes.byref(pv), 
                                       ctypes.byref(px), ctypes.byref(pud))

        if ec != 0: raise LabJackException(ec)          
        return pio.value, pchan.value, pv.value, px.value, pud.value
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def GetNextResult(Handle):
    """List All LabJack devices of a specific type over a specific connection type.

    For Windows only.

    Sample Usage (Shows getting the localID (8) and firmware version (1.27) of a U3 device):

    >>> u3Handle = OpenLabJack(LJ_dtU3, LJ_ctUSB, "0", 1)
    >>> AddRequest(u3Handle, LJ_ioGET_CONFIG, LJ_chLOCALID, 0, 0, 0)
    >>> AddRequest(u3Handle, LJ_ioGET_CONFIG, LJ_chFIRMWARE_VERSION, 0, 0, 0)
    >>> Go()
    >>> GetFirstResult(u3Handle)
    (1001, 0, 8.0, 0, 0.0)
    >>> GetNextResult(u3Handle)
    (1001, 11, 1.27, 0, 0.0)

    @type  DeviceType: number
    @param DeviceType: The LabJack device.
    @type  ConnectionType: number
    @param ConnectionType: The connection method (Ethernet/USB).
    
    @rtype: Tuple
    @return: The tuple (ioType, channel, value, x1, userData)
        - ioType (number): The io of the result.
        - serialNumber (number): The channel of the result.
        - value (number): The requested result.
        - x1 (number):  Used only in certain requests.
        - userData (number): Used only in certain requests.
        
    @raise LabJackException: 
    """ 
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pio = ctypes.c_long()
        pchan = ctypes.c_long()
        pv = ctypes.c_double()
        px = ctypes.c_long()
        pud = ctypes.c_double()
        ec = staticLib.GetNextResult(Handle, ctypes.byref(pio), 
                                       ctypes.byref(pchan), ctypes.byref(pv), 
                                       ctypes.byref(px), ctypes.byref(pud))

        if ec != 0: raise LabJackException(ec)          
        return pio.value, pchan.value, pv.value, px.value, pud.value
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows
def DoubleToStringAddress(number):
    """Converts a number (base 10) to an IP string.
    
    For Windows

    Sample Usage:

    >>> DoubleToStringAddress(3232235985)
    '192.168.1.209'
    
    @type  number: number
    @param number: Number to be converted.
    
    @rtype: String
    @return: The IP string converted from the number (base 10).
        
    @raise LabJackException: 
    """ 
    number = int(number)
    address = "%i.%i.%i.%i" % ((number >> 8*3 & 0xFF), (number >> 8*2 & 0xFF), (number >> 8 & 0xFF), (number & 0xFF))
    return address

def StringToDoubleAddress(pString):
    """Converts an IP string to a number (base 10).

    Sample Usage:

    >>> StringToDoubleAddress("192.168.1.209")
    3232235985L
    
    @type  pString: String
    @param pString: String to be converted.
    
    @rtype: number
    @return: The number (base 10) that represents the IP string.
        
    @raise LabJackException: 
    """  
    parts = pString.split('.')
    
    if len(parts) is not 4:
        raise LabJackException(0, "IP address not correctly formatted")
    
    try:
        value = (int(parts[0]) << 8*3) + (int(parts[1]) << 8*2) + (int(parts[2]) << 8) + int(parts[3])
    except ValueError:
        raise LabJackException(0, "IP address not correctly formatted")
    
    return value

#Windows
def StringToConstant(pString):
    """Converts an LabJackUD valid string to its constant value.

    For Windows

    Sample Usage:

    >>> StringToConstant("LJ_dtU3")
    3
    
    @type  pString: String
    @param pString: String to be converted.
    
    @rtype: number
    @return: The number (base 10) that represents the LabJackUD string.
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        a = ctypes.create_string_buffer(pString, 256)
        return staticLib.StringToConstant(a)
    else:
       raise LabJackException(0, "Function only supported for Windows")


# To hold all the error codes and what they mean:
ERROR_TO_STRING_DICT = dict()
ERROR_TO_STRING_DICT['1'] = ("SCRATCH_WRT_FAIL", "")
ERROR_TO_STRING_DICT['2'] = ("SCRATCH_ERASE_FAIL", "")
ERROR_TO_STRING_DICT['3'] = ("DATA_BUFFER_OVERFLOW", "")
ERROR_TO_STRING_DICT['4'] = ("ADC0_BUFFER_OVERFLOW", "")
ERROR_TO_STRING_DICT['5'] = ("FUNCTION_INVALID", "")
ERROR_TO_STRING_DICT['6'] = ("SWDT_TIME_INVALID", "This error is caused when an invalid time was passed to the watchdog.")
ERROR_TO_STRING_DICT['7'] = ("XBR_CONFIG_ERROR", "")
ERROR_TO_STRING_DICT['16'] = ("FLASH_WRITE_FAIL", "For some reason, the LabJack was unable to write the specified page of its internal flash.")
ERROR_TO_STRING_DICT['17'] = ("FLASH_ERASE_FAIL", "For some reason, the LabJack was unable to erase the specified page of its internal flash.")
ERROR_TO_STRING_DICT['18'] = ("FLASH_JMP_FAIL", "For some reason, the LabJack was unable to jump to a different section of flash. This may be an indication the flash is corrupted.")
ERROR_TO_STRING_DICT['19'] = ("FLASH_PSP_TIMEOUT", "")
ERROR_TO_STRING_DICT['20'] = ("FLASH_ABORT_RECEIVED", "")
ERROR_TO_STRING_DICT['21'] = ("FLASH_PAGE_MISMATCH", "")
ERROR_TO_STRING_DICT['22'] = ("FLASH_BLOCK_MISMATCH", "")
ERROR_TO_STRING_DICT['23'] = ("FLASH_PAGE_NOT_IN_CODE_AREA", "Usually, this error is raised when you try to write new firmware before upgrading the bootloader.")
ERROR_TO_STRING_DICT['24'] = ("MEM_ILLEGAL_ADDRESS", "")
ERROR_TO_STRING_DICT['25'] = ("FLASH_LOCKED", "Tried to write to flash before unlocking it.")
ERROR_TO_STRING_DICT['26'] = ("INVALID_BLOCK", "")
ERROR_TO_STRING_DICT['27'] = ("FLASH_ILLEGAL_PAGE", "")
ERROR_TO_STRING_DICT['28'] = ("FLASH_TOO_MANY_BYTES", "")
ERROR_TO_STRING_DICT['29'] = ("FLASH_INVALID_STRING_NUM", "")
ERROR_TO_STRING_DICT['40'] = ("SHT1x_COMM_TIME_OUT", "LabJack never received the ACK it was expecting from the SHT. This is usually due to incorrect wiring. Double check that all wires are securely connected to the correct pins.")
ERROR_TO_STRING_DICT['41'] = ("SHT1x_NO_ACK", "")
ERROR_TO_STRING_DICT['42'] = ("SHT1x_CRC_FAILED", "")
ERROR_TO_STRING_DICT['43'] = ("SHT1x_TOO_MANY_W_BYTES", "")
ERROR_TO_STRING_DICT['44'] = ("SHT1x_TOO_MANY_R_BYTES", "")
ERROR_TO_STRING_DICT['45'] = ("SHT1x_INVALID_MODE", "")
ERROR_TO_STRING_DICT['46'] = ("SHT1x_INVALID_LINE", "")
ERROR_TO_STRING_DICT['48'] = ("STREAM_IS_ACTIVE", "This error is raised when you call StreamStart after the stream has already been started.")
ERROR_TO_STRING_DICT['49'] = ("STREAM_TABLE_INVALID", "")
ERROR_TO_STRING_DICT['50'] = ("STREAM_CONFIG_INVALID", "")
ERROR_TO_STRING_DICT['52'] = ("STREAM_NOT_RUNNING", "This error is raised when you call StopStream after the stream has already been stopped.")
ERROR_TO_STRING_DICT['53'] = ("STREAM_INVALID_TRIGGER", "")
ERROR_TO_STRING_DICT['54'] = ("STREAM_ADC0_BUFFER_OVERFLOW", "")
ERROR_TO_STRING_DICT['55'] = ("STREAM_SCAN_OVERLAP", "This error is raised when a scan interrupt is fired before the LabJack has completed the previous scan. The most common cause of this error is a configuration with a high sampling rate and a large number of channels.")
ERROR_TO_STRING_DICT['56'] = ("STREAM_SAMPLE_NUM_INVALID", "")
ERROR_TO_STRING_DICT['57'] = ("STREAM_BIPOLAR_GAIN_INVALID", "")
ERROR_TO_STRING_DICT['58'] = ("STREAM_SCAN_RATE_INVALID", "")
ERROR_TO_STRING_DICT['59'] = ("STREAM_AUTORECOVER_ACTIVE", "This error is to inform you that the autorecover feature has been activated. Autorecovery is usually triggered by not reading data fast enough from the LabJack.")
ERROR_TO_STRING_DICT['60'] = ("STREAM_AUTORECOVER_REPORT", "This error marks the packet as an autorecovery report packet which contains how many packets were lost.")
ERROR_TO_STRING_DICT['63'] = ("STREAM_AUTORECOVER_OVERFLOW", "")
ERROR_TO_STRING_DICT['64'] = ("TIMER_INVALID_MODE", "")
ERROR_TO_STRING_DICT['65'] = ("TIMER_QUADRATURE_AB_ERROR", "")
ERROR_TO_STRING_DICT['66'] = ("TIMER_QUAD_PULSE_SEQUENCE", "")
ERROR_TO_STRING_DICT['67'] = ("TIMER_BAD_CLOCK_SOURCE", "")
ERROR_TO_STRING_DICT['68'] = ("TIMER_STREAM_ACTIVE", "")
ERROR_TO_STRING_DICT['69'] = ("TIMER_PWMSTOP_MODULE_ERROR", "")
ERROR_TO_STRING_DICT['70'] = ("TIMER_SEQUENCE_ERROR", "")
ERROR_TO_STRING_DICT['71'] = ("TIMER_LINE_SEQUENCE_ERROR", "")
ERROR_TO_STRING_DICT['72'] = ("TIMER_SHARING_ERROR", "")
ERROR_TO_STRING_DICT['80'] = ("EXT_OSC_NOT_STABLE", "")
ERROR_TO_STRING_DICT['81'] = ("INVALID_POWER_SETTING", "")
ERROR_TO_STRING_DICT['82'] = ("PLL_NOT_LOCKED", "")
ERROR_TO_STRING_DICT['96'] = ("INVALID_PIN", "")
ERROR_TO_STRING_DICT['97'] = ("PIN_CONFIGURED_FOR_ANALOG", "This error is raised when you try to do a digital operation on a pin that's configured for analog. Use a command like ConfigIO to set the pin to digital.")
ERROR_TO_STRING_DICT['98'] = ("PIN_CONFIGURED_FOR_DIGITAL", "This error is raised when you try to do an analog operation on a pin which is configured for digital. Use a command like ConfigIO to set the pin to analog.")
ERROR_TO_STRING_DICT['99'] = ("IOTYPE_SYNCH_ERROR", "")
ERROR_TO_STRING_DICT['100'] = ("INVALID_OFFSET", "")
ERROR_TO_STRING_DICT['101'] = ("IOTYPE_NOT_VALID", "")
ERROR_TO_STRING_DICT['102'] = ("TC_PIN_OFFSET_MUST_BE_4-8", "This error is raised when you try to configure the Timer/Counter pin offset to be 0-3.")

def lowlevelErrorToString( errorcode ):
    """Converts a low-level errorcode into a string.
    """
    try:
        name, advice = ERROR_TO_STRING_DICT[str(errorcode)]
    except KeyError:
        name = "UNKNOWN_ERROR"
        advice = "Unrecognized error code (%s)" % errorcode
    
    if advice is not "":
        msg = "%s (%s)\n%s" % (name, errorcode, advice)
    else:
        msg = "%s (%s)" % (name, errorcode)
        
    return msg

#Windows
def ErrorToString(ErrorCode):
    """Converts an LabJackUD valid error code to a String.

    For Windows

    Sample Usage:

    >>> ErrorToString(1007)
    'LabJack not found'
    
    @type  ErrorCode: number
    @param ErrorCode: Valid LabJackUD error code.
    
    @rtype: String
    @return: The string that represents the valid LabJackUD error code
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pString = ctypes.create_string_buffer(256)
        staticLib.ErrorToString(ctypes.c_long(ErrorCode), ctypes.byref(pString))
        return pString.value
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows, Linux, and Mac
def GetDriverVersion():
    """Converts an LabJackUD valid error code to a String.

    For Windows, Linux, and Mac

    Sample Usage:

    >>> GetDriverVersion()
    2.64
    
    >>> GetDriverVersion()
    Mac
    
    @rtype: number/String
    @return: Value of the driver version as a String
        - For Mac machines the return type is "Mac"
        - For Windows and Linux systems the return type is a number that represents the driver version
    """
    
    if os.name == 'nt':        
        staticLib.GetDriverVersion.restype = ctypes.c_float
        return str(staticLib.GetDriverVersion())
        
    elif os.name == 'posix':
        staticLib.LJUSB_GetLibraryVersion.restype = ctypes.c_float
        return "%.2f" % staticLib.LJUSB_GetLibraryVersion()
        
#Windows
def TCVoltsToTemp(TCType, TCVolts, CJTempK):
    """Converts a thermo couple voltage reading to an appropriate temperature reading.

    For Windows

    Sample Usage:

    >>> TCVoltsToTemp(LJ_ttK, 0.003141592, 297.038889)
    373.13353222244825
            
    @type  TCType: number
    @param TCType: The type of thermo couple used.
    @type  TCVolts: number
    @param TCVolts: The voltage reading from the thermo couple
    @type  CJTempK: number
    @param CJTempK: The cold junction temperature reading in Kelvin
    
    @rtype: number
    @return: The thermo couples temperature reading
        - pTCTempK
        
    @raise LabJackException:
    """
    if os.name == 'nt':
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        pTCTempK = ctypes.c_double()
        ec = staticLib.TCVoltsToTemp(ctypes.c_long(TCType), ctypes.c_double(TCVolts), 
                                     ctypes.c_double(CJTempK), ctypes.byref(pTCTempK))

        if ec != 0: raise LabJackException(ec)          
        return pTCTempK.value
    else:
       raise LabJackException(0, "Function only supported for Windows")


#Windows 
def Close():
    """Resets the driver and closes all open handles.

    For Windows

    Sample Usage:

    >>> Close()
            
    @rtype: None
    @return: The function returns nothing.
    """    

    opSys = os.name
    
    if(opSys == 'nt'):
        staticLib = ctypes.windll.LoadLibrary("labjackud")
        staticLib.Close()
    else:
       raise LabJackException(0, "Function only supported for Windows")

#Windows, Linux and Mac
def DriverPresent():
    try:
        ctypes.windll.LoadLibrary("labjackud")
        return True
    except:
        try:
            ctypes.cdll.LoadLibrary("liblabjackusb.so")
            return True
        except:
            try:
                ctypes.cdll.LoadLibrary("liblabjackusb.dylib")
                return True
            except:
                return False
            return False
        return False
        
def U12DriverPresent():
    try:
        ctypes.windll.LoadLibrary("ljackuw")
        return True
    except:
        return False


#Windows only
def LJHash(hashStr, size):
    """An approximation of the md5 hashing algorithms.  

    For Windows
    
    An approximation of the md5 hashing algorithm.  Used 
    for authorizations on UE9 version 1.73 and higher and u3 
    version 1.35 and higher.

    @type  hashStr: String
    @param hashStr: String to be hashed.
    @type  size: number
    @param size: Amount of bytes to hash from the hashStr
            
    @rtype: String
    @return: The hashed string.
    """  
    
    print "Hash String:" + str(hashStr)
    
    outBuff = (ctypes.c_char * 16)()
    retBuff = ''
    
    staticLib = ctypes.windll.LoadLibrary("labjackud")
    
    ec = staticLib.LJHash(ctypes.cast(hashStr, ctypes.POINTER(ctypes.c_char)),
                          size, 
                          ctypes.cast(outBuff, ctypes.POINTER(ctypes.c_char)), 
                          0)
    if ec != 0: raise LabJackException(ec)

    for i in range(16):
        retBuff += outBuff[i]
        
    return retBuff
    
    
    
    
    
    
def __listAllUE9Unix(connectionType):
    """Private listAll function for use on unix and mac machines to find UE9s.
    """

    deviceList = {}
    rcvDataBuff = []

    if connectionType == LJ_ctUSB:
        numDevices = staticLib.LJUSB_GetDevCount(LJ_dtUE9)
    
        for i in xrange(numDevices):
            try:
                device = openLabJack(LJ_dtUE9, 1, firstFound = False, devNumber = i+1)
                device.close()
            
                deviceList[str(device.serialNumber)] = device.__dict__
            except LabJackException:
                pass

    elif connectionType == LJ_ctETHERNET:
        #Create a socket
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        s.settimeout(BROADCAST_SOCKET_TIMEOUT)

        sndDataBuff = [0] * 6
        sndDataBuff[0] = 0x22
        sndDataBuff[1] = 0x78
        sndDataBuff[3] = 0xa9

        outBuff = ""
        for item in sndDataBuff:
            outBuff += chr(item)
        s.sendto(outBuff, ("255.255.255.255", 52362))

        try:
            while True:
                rcvDataBuff = s.recv(128)
                try:
                    rcvDataBuff = [ord(val) for val in rcvDataBuff]
                    if verifyChecksum(rcvDataBuff):
                        #Parse the packet
                        macAddress = rcvDataBuff[28:34]
                        macAddress.reverse()

                        # The serial number is four bytes:
                        # 0x10 and the last three bytes of the MAC address
                        serialBytes = chr(0x10)
                        for j in macAddress[3:]:
                            serialBytes += chr(j)
                        serial = struct.unpack(">I", serialBytes)[0]

                        #Parse out the IP address
                        ipAddress = ""
                        for j in range(13, 9, -1):
                            ipAddress += str(int(rcvDataBuff[j]))
                            ipAddress += "." 
                        ipAddress = ipAddress[0:-1]

                        #Local ID
                        localId = rcvDataBuff[8] & 0xff

                        deviceList[serial] = dict(devType = LJ_dtUE9, localId = localId, \
                                                    serialNumber = serial, ipAddress = ipAddress)
                except Exception, e:
                    pass
        except:
            pass

    return deviceList



def __listAllU3Unix():
    """Private listAll function for unix and mac machines.  Works on the U3 only.
    """
    deviceList = {}
    numDevices = staticLib.LJUSB_GetDevCount(LJ_dtU3)

    for i in xrange(numDevices):
        try:
            device = openLabJack(LJ_dtU3, 1, firstFound = False, devNumber = i+1)
            device.close()
            
            deviceList[str(device.serialNumber)] = device.__dict__
        except LabJackException:
            pass
        

    return deviceList


def __listAllU6Unix():
    """ List all for U6s """
    deviceList = {}
    numDevices = staticLib.LJUSB_GetDevCount(LJ_dtU6)

    for i in xrange(numDevices):
        try:
            device = openLabJack(LJ_dtU6, 1, firstFound = False, devNumber = i+1)
            device.close()
        
            deviceList[str(device.serialNumber)] = device.__dict__
        except LabJackException:
            pass

    return deviceList
    
def __listAllBridgesUnix():
    """ List all for Bridges """
    deviceList = {}
    numDevices = staticLib.LJUSB_GetDevCount(0x501)

    for i in xrange(numDevices):
        try:
            device = openLabJack(0x501, 1, firstFound = False, devNumber = i+1)
            device.close()
        
            deviceList[str(device.serialNumber)] = device.__dict__
        except LabJackException:
            pass

    return deviceList

def setChecksum16(buffer):
    total = 0;

    for i in range(6, len(buffer)):
        total += (buffer[i] & 0xff)

    buffer[4] = (total & 0xff)
    buffer[5] = ((total >> 8) & 0xff)

    return buffer


def setChecksum8(buffer, numBytes):
    total = 0

    for i in range(1, numBytes):
        total += (buffer[i] & 0xff)

    buffer[0] = (total & 0xff) + ((total >> 8) & 0xff)
    buffer[0] = (buffer[0] & 0xff) + ((buffer[0] >> 8) & 0xff)

    return buffer


class LJSocketHandle(object):
    """
    Class to replace a device handle with a socket to a LJSocket server.
    """
    def __init__(self, ipAddress, port, devType, firstFound, pAddress):
        try:
            serverSocket = socket.socket()
            serverSocket.connect((ipAddress, port))
            serverSocket.settimeout(SOCKET_TIMEOUT)
            
            f = serverSocket.makefile(bufsize = 0)
            f.write("scan\r\n")
            
            l = f.readline().strip()
            try:
                status, numLines = l.split(' ')
            except ValueError:
                raise Exception("Got invalid line from server: %s" % l)
                
            if status.lower().startswith('ok'):
                lines = []
                marked = None
                for i in range(int(numLines)):
                    l = f.readline().strip()
                    dev = parseline(l)
                    
                    if devType == dev['prodId']:
                        lines.append(dev)
                        
                        if not firstFound and (dev['localId'] == pAddress or dev['serial'] == pAddress):
                            marked = dev                 
                
                f.close()
                serverSocket.close()
                
                #print "Result of scan:"
                #print lines
                
                if firstFound and len(lines) > 0:
                    marked = lines[0]
                elif marked is not None:
                    pass
                else:
                    raise Exception("LabJack not found.")
                    
                if marked['crPort'] != 'x':
                    self.crSocket = socket.socket()
                    self.crSocket.connect((ipAddress, marked['crPort']))
                    self.crSocket.settimeout(LJSOCKET_TIMEOUT)
                else:
                    self.crSocket = None
                    
                if marked['modbusPort'] != 'x':
                    self.modbusSocket = socket.socket()
                    self.modbusSocket.connect((ipAddress, marked['modbusPort']))
                    self.modbusSocket.settimeout(LJSOCKET_TIMEOUT)
                else:
                    self.modbusSocket = None
                    
                if marked['spontPort'] != 'x':
                    self.spontSocket = socket.socket()
                    self.spontSocket.connect((ipAddress, marked['spontPort']))
                    self.spontSocket.settimeout(LJSOCKET_TIMEOUT)
                else:
                    self.spontSocket = None
                
            else:
                raise Exception("Got an error from LJSocket. It said '%s'" % l)
            
        except Exception, e:
            raise LabJackException(ec = LJE_LABJACK_NOT_FOUND, errorString = "Couldn't connect to a LabJack at %s:%s. The error was: %s" % (ipAddress, port, str(e)))
    
    def close(self):
        if self.crSocket is not None:
            self.crSocket.close()
            
        if self.modbusSocket is not None:
            self.modbusSocket.close()
            
        if self.spontSocket is not None:
            self.spontSocket.close()

        
def parseline(line):
    try:
        prodId, crPort, modbusPort, spontPort, localId, serial = line.split(' ')
        if not crPort.startswith('x'):
            crPort = int(crPort)
        if not modbusPort.startswith('x'):
            modbusPort = int(modbusPort)
        if not spontPort.startswith('x'):
            spontPort = int(spontPort)
            
    except ValueError:
        raise Exception("")
    
    return { 'prodId' : int(prodId), 'crPort' : crPort, 'modbusPort' : modbusPort, 'spontPort' : spontPort, 'localId' : int(localId), 'serial' : int(serial)  }


#Class for handling UE9 TCP Connections
class UE9TCPHandle(object):
    """__UE9TCPHandle(ipAddress)

    Creates two sockets for the streaming and non streaming port on the UE9.  
    Only works on default ports (Data 52360, Stream 52361).
    """

    def __init__(self, ipAddress, timeout = SOCKET_TIMEOUT):
        try:
            self.data = socket.socket()
            self.data.connect((ipAddress, 52360))
            self.data.settimeout(timeout)
        
            self.stream = socket.socket()
            self.stream.connect((ipAddress, 52361))
            self.stream.settimeout(timeout)
            
            try:
                self.modbus = socket.socket()
                self.modbus.connect((ipAddress, 502))
                self.modbus.settimeout(timeout)
            except socket.error, e:
                raise LabJackException("Couldn't connect to the Modbus port on the UE9. Please upgrade to UE9 Comm firmware to 1.43 or higher.")
        except LabJackException, e:
            raise e
        except Exception, e:
            print e
            raise LabJackException("Couldn't open sockets to the UE9 at IP Address %s. Error was: %s" % (ipAddress, e))
        

    def close(self):
        try:
            self.data.close()
            self.stream.close()
            self.modbus.close()
        except Exception, e:
            print "UE9 Handle close exception: ", e
            pass


    
def toDouble(bytes):
    """
    Name: toDouble(buffer)
    Args: buffer, an array with 8 bytes
    Desc: Converts the 8 byte array into a floating point number.
    """
    right, left = struct.unpack("<Ii", struct.pack("B" * 8, *bytes[0:8]))
    
    return float(left) + float(right)/(2**32)
    
def hexWithoutQuotes(l):
    """ Return a string listing hex without all the single quotes.
    
    >>> l = range(10)
    >>> print hexWithoutQuotes(l)
    [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9]

    """
    return str([hex (i) for i in l]).replace("'", "")
    
#device types
LJ_dtUE9 = 9
"""Device type for the UE9"""

LJ_dtU3 = 3
"""Device type for the U3"""

LJ_dtU6 = 6
"""Device type for the U6"""

# connection types:
LJ_ctUSB = 1 # UE9 + U3
"""Connection type for the UE9 and U3"""
LJ_ctETHERNET = 2 # UE9 only
"""Connection type for the UE9"""

LJ_ctUSB_RAW = 101 # UE9 + U3
"""Connection type for the UE9 and U3

Raw connection types are used to open a device but not communicate with it
should only be used if the normal connection types fail and for testing.
If a device is opened with the raw connection types, only LJ_ioRAW_OUT
and LJ_ioRAW_IN io types should be used
"""

LJ_ctETHERNET_RAW = 102 # UE9 only
"""Connection type for the UE9

Raw connection types are used to open a device but not communicate with it
should only be used if the normal connection types fail and for testing.
If a device is opened with the raw connection types, only LJ_ioRAW_OUT
and LJ_ioRAW_IN io types should be used
"""

LJ_ctLJSOCKET = 200
"""Connection type for USB LabJack connected to LJSocket server.
"""

# io types:
LJ_ioGET_AIN = 10 # UE9 + U3.  This is single ended version.
"""IO type for the UE9 and U3

This is the single ended version
"""  

LJ_ioGET_AIN_DIFF = 15 # U3 only.  Put second channel in x1.  If 32 is passed as x1, Vref will be added to the result. 
"""IO type for the U3

Put second channel in x1.  If 32 is passed as x1, Vref will be added to the result. 
"""

LJ_ioPUT_AIN_RANGE = 2000 # UE9
"""IO type for the UE9"""

LJ_ioGET_AIN_RANGE = 2001 # UE9
"""IO type for the UE9"""

# sets or reads the analog or digital mode of the FIO and EIO pins.     FIO is Channel 0-7, EIO 8-15
LJ_ioPUT_ANALOG_ENABLE_BIT = 2013 # U3 
"""IO type for the U3

Sets or reads the analog or digital mode of the FIO and EIO pins.     FIO is Channel 0-7, EIO 8-15
"""

LJ_ioGET_ANALOG_ENABLE_BIT = 2014 # U3 
"""IO type for the U3

Sets or reads the analog or digital mode of the FIO and EIO pins.     FIO is Channel 0-7, EIO 8-15
"""


# sets or reads the analog or digital mode of the FIO and EIO pins. Channel is starting 
# bit #, x1 is number of bits to read. The pins are set by passing a bitmask as a double
# for the value.  The first bit of the int that the double represents will be the setting 
# for the pin number sent into the channel variable. 
LJ_ioPUT_ANALOG_ENABLE_PORT = 2015 # U3 
""" IO type for the U3

sets or reads the analog or digital mode of the FIO and EIO pins. Channel is starting 
bit #, x1 is number of bits to read. The pins are set by passing a bitmask as a double
for the value.  The first bit of the int that the double represents will be the setting 
for the pin number sent into the channel variable.
"""

LJ_ioGET_ANALOG_ENABLE_PORT = 2016 # U3
""" IO type for the U3

sets or reads the analog or digital mode of the FIO and EIO pins. Channel is starting 
bit #, x1 is number of bits to read. The pins are set by passing a bitmask as a double
for the value.  The first bit of the int that the double represents will be the setting 
for the pin number sent into the channel variable.
"""


LJ_ioPUT_DAC = 20 # UE9 + U3
"""IO type for the U3 and UE9"""
LJ_ioPUT_DAC_ENABLE = 2002 # UE9 + U3 (U3 on Channel 1 only)
"""IO type for the U3 and UE9

U3 on channel 1 only.
"""
LJ_ioGET_DAC_ENABLE = 2003 # UE9 + U3 (U3 on Channel 1 only)
"""IO type for the U3 and UE9

U3 on channel 1 only.
"""

LJ_ioGET_DIGITAL_BIT = 30 # UE9 + U3  # changes direction of bit to input as well
LJ_ioGET_DIGITAL_BIT_DIR = 31 # U3
LJ_ioGET_DIGITAL_BIT_STATE = 32 # does not change direction of bit, allowing readback of output

# channel is starting bit #, x1 is number of bits to read 
LJ_ioGET_DIGITAL_PORT = 35 # UE9 + U3  # changes direction of bits to input as well
LJ_ioGET_DIGITAL_PORT_DIR = 36 # U3
LJ_ioGET_DIGITAL_PORT_STATE = 37 # U3 does not change direction of bits, allowing readback of output

# digital put commands will set the specified digital line(s) to output
LJ_ioPUT_DIGITAL_BIT = 40 # UE9 + U3
# channel is starting bit #, value is output value, x1 is bits to write
LJ_ioPUT_DIGITAL_PORT = 45 # UE9 + U3

# Used to create a pause between two events in a U3 low-level feedback
# command.    For example, to create a 100 ms positive pulse on FIO0, add a
# request to set FIO0 high, add a request for a wait of 100000, add a
# request to set FIO0 low, then Go.     Channel is ignored.  Value is
# microseconds to wait and should range from 0 to 8388480.    The actual
# resolution of the wait is 128 microseconds.
LJ_ioPUT_WAIT = 70 # U3

# counter.    Input only.
LJ_ioGET_COUNTER = 50 # UE9 + U3

LJ_ioPUT_COUNTER_ENABLE = 2008 # UE9 + U3
LJ_ioGET_COUNTER_ENABLE = 2009 # UE9 + U3


# this will cause the designated counter to reset.    If you want to reset the counter with
# every read, you have to use this command every time.
LJ_ioPUT_COUNTER_RESET = 2012  # UE9 + U3 


# on UE9: timer only used for input. Output Timers don't use these.     Only Channel used.
# on U3: Channel used (0 or 1).     
LJ_ioGET_TIMER = 60 # UE9 + U3

LJ_ioPUT_TIMER_VALUE = 2006 # UE9 + U3.     Value gets new value
LJ_ioPUT_TIMER_MODE = 2004 # UE9 + U3.    On both Value gets new mode.  
LJ_ioGET_TIMER_MODE = 2005 # UE9

# IOTypes for use with SHT sensor.    For LJ_ioSHT_GET_READING, a channel of LJ_chSHT_TEMP (5000) will 
# read temperature, and LJ_chSHT_RH (5001) will read humidity.    
# The LJ_ioSHT_DATA_CHANNEL and LJ_ioSHT_SCK_CHANNEL iotypes use the passed channel 
# to set the appropriate channel for the data and SCK lines for the SHT sensor. 
# Default digital channels are FIO0 for the data channel and FIO1 for the clock channel. 
LJ_ioSHT_GET_READING = 500 # UE9 + U3.
LJ_ioSHT_DATA_CHANNEL = 501 # UE9 + U3. Default is FIO0
LJ_ioSHT_CLOCK_CHANNEL = 502 # UE9 + U3. Default is FIO1

# Uses settings from LJ_chSPI special channels (set with LJ_ioPUT_CONFIG) to communcaite with
# something using an SPI interface.     The value parameter is the number of bytes to transfer
# and x1 is the address of the buffer.    The data from the buffer will be sent, then overwritten
# with the data read.  The channel parameter is ignored. 
LJ_ioSPI_COMMUNICATION = 503 # UE9
LJ_ioI2C_COMMUNICATION = 504 # UE9 + U3
LJ_ioASYNCH_COMMUNICATION = 505 # UE9 + U3
LJ_ioTDAC_COMMUNICATION = 506 # UE9 + U3

# Set's the U3 to it's original configuration.    This means sending the following
# to the ConfigIO and TimerClockConfig low level functions
#
# ConfigIO
# Byte #
# 6          WriteMask          15      Write all parameters.
# 8          TimerCounterConfig      0          No timers/counters.  Offset=0.
# 9          DAC1Enable      0          DAC1 disabled.
# 10      FIOAnalog          0          FIO all digital.
# 11      EIOAnalog          0          EIO all digital.
# 
# 
# TimerClockConfig
# Byte #
# 8          TimerClockConfig          130      Set clock to 24 MHz.
# 9          TimerClockDivisor          0          Divisor = 0.

# 
LJ_ioPIN_CONFIGURATION_RESET = 2017 # U3

# the raw in/out are unusual, channel # corresponds to the particular comm port, which 
# depends on the device.  For example, on the UE9, 0 is main comm port, and 1 is the streaming comm.
# Make sure and pass a porter to a char buffer in x1, and the number of bytes desired in value.     A call 
# to GetResult will return the number of bytes actually read/written.  The max you can send out in one call
# is 512 bytes to the UE9 and 16384 bytes to the U3.
LJ_ioRAW_OUT = 100 # UE9 + U3
LJ_ioRAW_IN = 101 # UE9 + U3
# sets the default power up settings based on the current settings of the device AS THIS DLL KNOWS.     This last part
# basically means that you should set all parameters directly through this driver before calling this.    This writes 
# to flash which has a limited lifetime, so do not do this too often.  Rated endurance is 20,000 writes.
LJ_ioSET_DEFAULTS = 103 # U3

# requests to create the list of channels to stream.  Usually you will use the CLEAR_STREAM_CHANNELS request first, which
# will clear any existing channels, then use ADD_STREAM_CHANNEL multiple times to add your desired channels.  Some devices will 
# use value, x1 for other parameters such as gain.    Note that you can do CLEAR, and then all your ADDs in a single Go() as long
# as you add the requests in order.
LJ_ioADD_STREAM_CHANNEL = 200
LJ_ioCLEAR_STREAM_CHANNELS = 201
LJ_ioSTART_STREAM = 202
LJ_ioSTOP_STREAM = 203
 
LJ_ioADD_STREAM_CHANNEL_DIFF = 206

# Get stream data has several options.    If you just want to get a single channel's data (if streaming multiple channels), you 
# can pass in the desired channel #, then the number of data points desired in Value, and a pointer to an array to put the 
# data into as X1.    This array needs to be an array of doubles. Therefore, the array needs to be 8 * number of 
# requested data points in byte length. What is returned depends on the StreamWaitMode.     If None, this function will only return 
# data available at the time of the call.  You therefore must call GetResult() for this function to retrieve the actually number 
# of points retreived.    If Pump or Sleep, it will return only when the appropriate number of points have been read or no 
# new points arrive within 100ms.  Since there is this timeout, you still need to use GetResult() to determine if the timeout 
# occured.    If AllOrNone, you again need to check GetResult.  

# You can also retreive the entire scan by passing LJ_chALL_CHANNELS.  In this case, the Value determines the number of SCANS 
# returned, and therefore, the array must be 8 * number of scans requested * number of channels in each scan.  Likewise
# GetResult() will return the number of scans, not the number of data points returned.

# Note: data is stored interleaved across all streaming channels.  In other words, if you are streaming two channels, 0 and 1, 
# and you request LJ_chALL_CHANNELS, you will get, Channel0, Channel1, Channel0, Channel1, etc.     Once you have requested the 
# data, any data returned is removed from the internal buffer, and the next request will give new data.

# Note: if reading the data channel by channel and not using LJ_chALL_CHANNELS, the data is not removed from the internal buffer
# until the data from the last channel in the scan is requested.  This means that if you are streaming three channels, 0, 1 and 2,
# and you request data from channel 0, then channel 1, then channel 0 again, the request for channel 0 the second time will 
# return the exact same amount of data.     Also note, that the amount of data that will be returned for each channel request will be
# the same until you've read the last channel in the scan, at which point your next block may be a different size.

# Note: although more convenient, requesting individual channels is slightly slower then using LJ_chALL_CHANNELS.  Since you 
# are probably going to have to split the data out anyway, we have saved you the trouble with this option.    

# Note: if you are only scanning one channel, the Channel parameter is ignored.

LJ_ioGET_STREAM_DATA = 204
        
# U3 only:

# Channel = 0 buzz for a count, Channel = 1 buzz continuous
# Value is the Period
# X1 is the toggle count when channel = 0
LJ_ioBUZZER = 300 # U3 

# config iotypes:
LJ_ioPUT_CONFIG = 1000 # UE9 + U3
LJ_ioGET_CONFIG = 1001 # UE9 + U3


# channel numbers used for CONFIG types:
# UE9 + U3
LJ_chLOCALID = 0 # UE9 + U3
LJ_chHARDWARE_VERSION = 10 # UE9 + U3 (Read Only)
LJ_chSERIAL_NUMBER = 12 # UE9 + U3 (Read Only)
LJ_chFIRMWARE_VERSION = 11 # UE9 + U3 (Read Only)
LJ_chBOOTLOADER_VERSION = 15 # UE9 + U3 (Read Only)

# UE9 specific:
LJ_chCOMM_POWER_LEVEL = 1 #UE9
LJ_chIP_ADDRESS = 2 #UE9
LJ_chGATEWAY = 3 #UE9
LJ_chSUBNET = 4 #UE9
LJ_chPORTA = 5 #UE9
LJ_chPORTB = 6 #UE9
LJ_chDHCP = 7 #UE9
LJ_chPRODUCTID = 8 #UE9
LJ_chMACADDRESS = 9 #UE9
LJ_chCOMM_FIRMWARE_VERSION = 11     
LJ_chCONTROL_POWER_LEVEL = 13 #UE9 
LJ_chCONTROL_FIRMWARE_VERSION = 14 #UE9 (Read Only)
LJ_chCONTROL_BOOTLOADER_VERSION = 15 #UE9 (Read Only)
LJ_chCONTROL_RESET_SOURCE = 16 #UE9 (Read Only)
LJ_chUE9_PRO = 19 # UE9 (Read Only)

# U3 only:
# sets the state of the LED 
LJ_chLED_STATE = 17 # U3   value = LED state
LJ_chSDA_SCL = 18 # U3     enable / disable SDA/SCL as digital I/O


# Used to access calibration and user data.     The address of an array is passed in as x1.
# For the UE9, a 1024-element buffer of bytes is passed for user data and a 128-element
# buffer of doubles is passed for cal constants.
# For the U3, a 256-element buffer of bytes is passed for user data and a 12-element
# buffer of doubles is passed for cal constants.
# The layout of cal ants are defined in the users guide for each device.
# When the LJ_chCAL_CONSTANTS special channel is used with PUT_CONFIG, a
# special value (0x4C6C) must be passed in to the Value parameter. This makes it
# more difficult to accidently erase the cal constants.     In all other cases the Value
# parameter is ignored.
LJ_chCAL_CONSTANTS = 400 # UE9 + U3
LJ_chUSER_MEM = 402 # UE9 + U3

# Used to write and read the USB descriptor strings.  This is generally for OEMs
# who wish to change the strings.
# Pass the address of an array in x1.  Value parameter is ignored.
# The array should be 128 elements of bytes.  The first 64 bytes are for the
# iManufacturer string, and the 2nd 64 bytes are for the iProduct string.
# The first byte of each 64 byte block (bytes 0 and 64) contains the number
# of bytes in the string.  The second byte (bytes 1 and 65) is the USB spec
# value for a string descriptor (0x03).     Bytes 2-63 and 66-127 contain unicode
# encoded strings (up to 31 characters each).
LJ_chUSB_STRINGS = 404 # U3


# timer/counter related
LJ_chNUMBER_TIMERS_ENABLED = 1000 # UE9 + U3
LJ_chTIMER_CLOCK_BASE = 1001 # UE9 + U3
LJ_chTIMER_CLOCK_DIVISOR = 1002 # UE9 + U3
LJ_chTIMER_COUNTER_PIN_OFFSET = 1003 # U3

# AIn related
LJ_chAIN_RESOLUTION = 2000 # ue9 + u3
LJ_chAIN_SETTLING_TIME = 2001 # ue9 + u3
LJ_chAIN_BINARY = 2002 # ue9 + u3

# DAC related
LJ_chDAC_BINARY = 3000 # ue9 + u3

# SHT related
LJ_chSHT_TEMP = 5000 # ue9 + u3
LJ_chSHT_RH = 5001 # ue9 + u3

# SPI related
LJ_chSPI_AUTO_CS = 5100 # UE9
LJ_chSPI_DISABLE_DIR_CONFIG = 5101 # UE9
LJ_chSPI_MODE = 5102 # UE9
LJ_chSPI_CLOCK_FACTOR = 5103 # UE9
LJ_chSPI_MOSI_PINNUM = 5104 # UE9
LJ_chSPI_MISO_PINNUM = 5105 # UE9
LJ_chSPI_CLK_PINNUM = 5106 # UE9
LJ_chSPI_CS_PINNUM = 5107 # UE9

# I2C related :
# used with LJ_ioPUT_CONFIG
LJ_chI2C_ADDRESS_BYTE = 5108 # UE9 + U3
LJ_chI2C_SCL_PIN_NUM = 5109 # UE9 + U3
LJ_chI2C_SDA_PIN_NUM = 5110 # UE9 + U3
LJ_chI2C_OPTIONS = 5111 # UE9 + U3
LJ_chI2C_SPEED_ADJUST = 5112 # UE9 + U3

# used with LJ_ioI2C_COMMUNICATION :
LJ_chI2C_READ = 5113 # UE9 + U3
LJ_chI2C_WRITE = 5114 # UE9 + U3
LJ_chI2C_GET_ACKS = 5115 # UE9 + U3
LJ_chI2C_WRITE_READ = 5130 # UE9 + U3

# ASYNCH related :
# Used with LJ_ioASYNCH_COMMUNICATION
LJ_chASYNCH_RX = 5117 # UE9 + U3
LJ_chASYNCH_TX = 5118 # UE9 + U3
LJ_chASYNCH_FLUSH = 5128 # UE9 + U3
LJ_chASYNCH_ENABLE = 5129 # UE9 + U3

# Used with LJ_ioPUT_CONFIG and LJ_ioGET_CONFIG
LJ_chASYNCH_BAUDFACTOR = 5127 # UE9 + U3

# stream related.  Note, Putting to any of these values will stop any running streams.
LJ_chSTREAM_SCAN_FREQUENCY = 4000
LJ_chSTREAM_BUFFER_SIZE = 4001
LJ_chSTREAM_CLOCK_OUTPUT = 4002
LJ_chSTREAM_EXTERNAL_TRIGGER = 4003
LJ_chSTREAM_WAIT_MODE = 4004
# readonly stream related
LJ_chSTREAM_BACKLOG_COMM = 4105
LJ_chSTREAM_BACKLOG_CONTROL = 4106
LJ_chSTREAM_BACKLOG_UD = 4107
LJ_chSTREAM_SAMPLES_PER_PACKET = 4108


# special channel #'s
LJ_chALL_CHANNELS = -1
LJ_INVALID_CONSTANT = -999


#Thermocouple Type constants.
LJ_ttB = 6001
"""Type B thermocouple constant"""
LJ_ttE = 6002
"""Type E thermocouple constant"""
LJ_ttJ = 6003
"""Type J thermocouple constant"""
LJ_ttK = 6004
"""Type K thermocouple constant"""
LJ_ttN = 6005
"""Type N thermocouple constant"""
LJ_ttR = 6006
"""Type R thermocouple constant"""
LJ_ttS = 6007
"""Type S thermocouple constant"""
LJ_ttT = 6008
"""Type T thermocouple constant"""


# other constants:
# ranges (not all are supported by all devices):
LJ_rgBIP20V = 1     # -20V to +20V
LJ_rgBIP10V = 2     # -10V to +10V
LJ_rgBIP5V = 3     # -5V to +5V
LJ_rgBIP4V = 4     # -4V to +4V
LJ_rgBIP2P5V = 5 # -2.5V to +2.5V
LJ_rgBIP2V = 6     # -2V to +2V
LJ_rgBIP1P25V = 7# -1.25V to +1.25V
LJ_rgBIP1V = 8     # -1V to +1V
LJ_rgBIPP625V = 9# -0.625V to +0.625V

LJ_rgUNI20V = 101  # 0V to +20V
LJ_rgUNI10V = 102  # 0V to +10V
LJ_rgUNI5V = 103   # 0V to +5V
LJ_rgUNI4V = 104   # 0V to +4V
LJ_rgUNI2P5V = 105 # 0V to +2.5V
LJ_rgUNI2V = 106   # 0V to +2V
LJ_rgUNI1P25V = 107# 0V to +1.25V
LJ_rgUNI1V = 108   # 0V to +1V
LJ_rgUNIP625V = 109# 0V to +0.625V
LJ_rgUNIP500V = 110 # 0V to +0.500V
LJ_rgUNIP3125V = 111 # 0V to +0.3125V

# timer modes (UE9 only):
LJ_tmPWM16 = 0 # 16 bit PWM
LJ_tmPWM8 = 1 # 8 bit PWM
LJ_tmRISINGEDGES32 = 2 # 32-bit rising to rising edge measurement
LJ_tmFALLINGEDGES32 = 3 # 32-bit falling to falling edge measurement
LJ_tmDUTYCYCLE = 4 # duty cycle measurement
LJ_tmFIRMCOUNTER = 5 # firmware based rising edge counter
LJ_tmFIRMCOUNTERDEBOUNCE = 6 # firmware counter with debounce
LJ_tmFREQOUT = 7 # frequency output
LJ_tmQUAD = 8 # Quadrature
LJ_tmTIMERSTOP = 9 # stops another timer after n pulses
LJ_tmSYSTIMERLOW = 10 # read lower 32-bits of system timer
LJ_tmSYSTIMERHIGH = 11 # read upper 32-bits of system timer
LJ_tmRISINGEDGES16 = 12 # 16-bit rising to rising edge measurement
LJ_tmFALLINGEDGES16 = 13 # 16-bit falling to falling edge measurement

# timer clocks:
LJ_tc750KHZ = 0      # UE9: 750 khz 
LJ_tcSYS = 1      # UE9: system clock

LJ_tc2MHZ = 10       # U3: Hardware Version 1.20 or lower
LJ_tc6MHZ = 11       # U3: Hardware Version 1.20 or lower
LJ_tc24MHZ = 12        # U3: Hardware Version 1.20 or lower
LJ_tc500KHZ_DIV = 13# U3: Hardware Version 1.20 or lower
LJ_tc2MHZ_DIV = 14    # U3: Hardware Version 1.20 or lower
LJ_tc6MHZ_DIV = 15    # U3: Hardware Version 1.20 or lower
LJ_tc24MHZ_DIV = 16 # U3: Hardware Version 1.20 or lower

# stream wait modes
LJ_swNONE = 1  # no wait, return whatever is available
LJ_swALL_OR_NONE = 2 # no wait, but if all points requested aren't available, return none.
LJ_swPUMP = 11    # wait and pump the message pump.  Prefered when called from primary thread (if you don't know
                           # if you are in the primary thread of your app then you probably are.  Do not use in worker
                           # secondary threads (i.e. ones without a message pump).
LJ_swSLEEP = 12 # wait by sleeping (don't do this in the primary thread of your app, or it will temporarily 
                           # hang)    This is usually used in worker secondary threads.


# BETA CONSTANTS
# Please note that specific usage of these constants and their values might change

# SWDT related 
LJ_chSWDT_RESET_COMM = 5200 # UE9 - Reset Comm on watchdog reset
LJ_chSWDT_RESET_CONTROL = 5201 # UE9 - Reset Control on watchdog trigger
LJ_chSWDT_UDPATE_DIO0 = 5202 # UE9 - Update DIO0 settings after reset
LJ_chSWDT_UPDATE_DIO1 = 5203 # UE9 - Update DIO1 settings after reset
LJ_chSWDT_DIO0 = 5204 # UE9 - DIO0 channel and state (value) to be set after reset
LJ_chSWDT_DIO1 = 5205 # UE9 - DIO1 channel and state (value) to be set after reset
LJ_chSWDT_UPDATE_DAC0 = 5206 # UE9 - Update DAC1 settings after reset
LJ_chSWDT_UPDATE_DAC1 = 5207 # UE9 - Update DAC1 settings after reset
LJ_chSWDT_DAC0 = 5208 # UE9 - voltage to set DAC0 at on watchdog reset
LJ_chSWDT_DAC1 = 5209 # UE9 - voltage to set DAC1 at on watchdog reset
LJ_chSWDT_DACS_ENABLE = 5210 # UE9 - Enable DACs on watchdog reset
LJ_chSWDT_ENABLE = 5211 # UE9 - used with LJ_ioSWDT_CONFIG to enable watchdog.    Value paramter is number of seconds to trigger
LJ_chSWDT_DISABLE = 5212 # UE9 - used with LJ_ioSWDT_CONFIG to enable watchdog.

LJ_ioSWDT_CONFIG = 504 # UE9 - Use LJ_chSWDT_ENABLE or LJ_chSWDT_DISABLE

LJ_tc4MHZ = 20       # U3: Hardware Version 1.21 or higher
LJ_tc12MHZ = 21        # U3: Hardware Version 1.21 or higher
LJ_tc48MHZ = 22        # U3: Hardware Version 1.21 or higher
LJ_tc1000KHZ_DIV = 23# U3: Hardware Version 1.21 or higher
LJ_tc4MHZ_DIV = 24    # U3: Hardware Version 1.21 or higher
LJ_tc12MHZ_DIV = 25     # U3: Hardware Version 1.21 or higher
LJ_tc48MHZ_DIV = 26 # U3: Hardware Version 1.21 or higher

# END BETA CONSTANTS


# error codes:    These will always be in the range of -1000 to 3999 for labView compatibility (+6000)
LJE_NOERROR = 0
 
LJE_INVALID_CHANNEL_NUMBER = 2 # occurs when a channel that doesn't exist is specified (i.e. DAC #2 on a UE9), or data from streaming is requested on a channel that isn't streaming
LJE_INVALID_RAW_INOUT_PARAMETER = 3
LJE_UNABLE_TO_START_STREAM = 4
LJE_UNABLE_TO_STOP_STREAM = 5
LJE_NOTHING_TO_STREAM = 6
LJE_UNABLE_TO_CONFIG_STREAM = 7
LJE_BUFFER_OVERRUN = 8 # occurs when stream buffer overruns (this is the driver buffer not the hardware buffer).  Stream is stopped.
LJE_STREAM_NOT_RUNNING = 9
LJE_INVALID_PARAMETER = 10
LJE_INVALID_STREAM_FREQUENCY = 11 
LJE_INVALID_AIN_RANGE = 12
LJE_STREAM_CHECKSUM_ERROR = 13 # occurs when a stream packet fails checksum.  Stream is stopped
LJE_STREAM_COMMAND_ERROR = 14 # occurs when a stream packet has invalid command values.     Stream is stopped.
LJE_STREAM_ORDER_ERROR = 15 # occurs when a stream packet is received out of order (typically one is missing).    Stream is stopped.
LJE_AD_PIN_CONFIGURATION_ERROR = 16 # occurs when an analog or digital request was made on a pin that isn't configured for that type of request
LJE_REQUEST_NOT_PROCESSED = 17 # When a LJE_AD_PIN_CONFIGURATION_ERROR occurs, all other IO requests after the request that caused the error won't be processed. Those requests will return this error.


# U3 Specific Errors
LJE_SCRATCH_ERROR = 19
"""U3 error"""
LJE_DATA_BUFFER_OVERFLOW = 20
"""U3 error"""
LJE_ADC0_BUFFER_OVERFLOW = 21 
"""U3 error"""
LJE_FUNCTION_INVALID = 22
"""U3 error"""
LJE_SWDT_TIME_INVALID = 23
"""U3 error"""
LJE_FLASH_ERROR = 24
"""U3 error"""
LJE_STREAM_IS_ACTIVE = 25
"""U3 error"""
LJE_STREAM_TABLE_INVALID = 26
"""U3 error"""
LJE_STREAM_CONFIG_INVALID = 27
"""U3 error"""
LJE_STREAM_BAD_TRIGGER_SOURCE = 28
"""U3 error"""
LJE_STREAM_INVALID_TRIGGER = 30
"""U3 error"""
LJE_STREAM_ADC0_BUFFER_OVERFLOW = 31
"""U3 error"""
LJE_STREAM_SAMPLE_NUM_INVALID = 33
"""U3 error"""
LJE_STREAM_BIPOLAR_GAIN_INVALID = 34
"""U3 error"""
LJE_STREAM_SCAN_RATE_INVALID = 35
"""U3 error"""
LJE_TIMER_INVALID_MODE = 36
"""U3 error"""
LJE_TIMER_QUADRATURE_AB_ERROR = 37
"""U3 error"""
LJE_TIMER_QUAD_PULSE_SEQUENCE = 38
"""U3 error"""
LJE_TIMER_BAD_CLOCK_SOURCE = 39
"""U3 error"""
LJE_TIMER_STREAM_ACTIVE = 40
"""U3 error"""
LJE_TIMER_PWMSTOP_MODULE_ERROR = 41
"""U3 error"""
LJE_TIMER_SEQUENCE_ERROR = 42
"""U3 error"""
LJE_TIMER_SHARING_ERROR = 43
"""U3 error"""
LJE_TIMER_LINE_SEQUENCE_ERROR = 44
"""U3 error"""
LJE_EXT_OSC_NOT_STABLE = 45
"""U3 error"""
LJE_INVALID_POWER_SETTING = 46
"""U3 error"""
LJE_PLL_NOT_LOCKED = 47
"""U3 error"""
LJE_INVALID_PIN = 48
"""U3 error"""
LJE_IOTYPE_SYNCH_ERROR = 49
"""U3 error"""
LJE_INVALID_OFFSET = 50
"""U3 error"""
LJE_FEEDBACK_IOTYPE_NOT_VALID = 51
"""U3 error

Has been described as mearly a flesh wound.
"""

LJE_SHT_CRC = 52
LJE_SHT_MEASREADY = 53
LJE_SHT_ACK = 54
LJE_SHT_SERIAL_RESET = 55
LJE_SHT_COMMUNICATION = 56

LJE_AIN_WHILE_STREAMING = 57

LJE_STREAM_TIMEOUT = 58
LJE_STREAM_CONTROL_BUFFER_OVERFLOW = 59
LJE_STREAM_SCAN_OVERLAP = 60
LJE_FIRMWARE_DOESNT_SUPPORT_IOTYPE = 61
LJE_FIRMWARE_DOESNT_SUPPORT_CHANNEL = 62
LJE_FIRMWARE_DOESNT_SUPPORT_VALUE = 63


LJE_MIN_GROUP_ERROR = 1000 # all errors above this number will stop all requests, below this number are request level errors.

LJE_UNKNOWN_ERROR = 1001 # occurs when an unknown error occurs that is caught, but still unknown.
LJE_INVALID_DEVICE_TYPE = 1002 # occurs when devicetype is not a valid device type
LJE_INVALID_HANDLE = 1003 # occurs when invalid handle used
LJE_DEVICE_NOT_OPEN = 1004    # occurs when Open() fails and AppendRead called despite.
LJE_NO_DATA_AVAILABLE = 1005 # this is cause when GetData() called without calling DoRead(), or when GetData() passed channel that wasn't read
LJE_NO_MORE_DATA_AVAILABLE = 1006
LJE_LABJACK_NOT_FOUND = 1007 # occurs when the labjack is not found at the given id or address.
LJE_COMM_FAILURE = 1008 # occurs when unable to send or receive the correct # of bytes
LJE_CHECKSUM_ERROR = 1009
LJE_DEVICE_ALREADY_OPEN = 1010 
LJE_COMM_TIMEOUT = 1011
LJE_USB_DRIVER_NOT_FOUND = 1012
LJE_INVALID_CONNECTION_TYPE = 1013
LJE_INVALID_MODE = 1014


# warning are negative
LJE_DEVICE_NOT_CALIBRATED = -1 # defaults used instead
LJE_UNABLE_TO_READ_CALDATA = -2 # defaults used instead


# depreciated constants:
LJ_ioANALOG_INPUT = 10  
"""Deprecated constant"""  
LJ_ioANALOG_OUTPUT = 20 # UE9 + U3
"""Deprecated constant"""  
LJ_ioDIGITAL_BIT_IN = 30 # UE9 + U3
"""Deprecated constant"""  
LJ_ioDIGITAL_PORT_IN = 35 # UE9 + U3 
"""Deprecated constant"""  
LJ_ioDIGITAL_BIT_OUT = 40 # UE9 + U3
"""Deprecated constant"""  
LJ_ioDIGITAL_PORT_OUT = 45 # UE9 + U3
"""Deprecated constant"""  
LJ_ioCOUNTER = 50 # UE9 + U3
"""Deprecated constant"""  
LJ_ioTIMER = 60 # UE9 + U3
"""Deprecated constant"""  
LJ_ioPUT_COUNTER_MODE = 2010 # UE9
"""Deprecated constant"""  
LJ_ioGET_COUNTER_MODE = 2011 # UE9
"""Deprecated constant"""  
LJ_ioGET_TIMER_VALUE = 2007 # UE9
"""Deprecated constant"""  
LJ_ioCYCLE_PORT = 102  # UE9 
"""Deprecated constant"""  
LJ_chTIMER_CLOCK_CONFIG = 1001 # UE9 + U3 
"""Deprecated constant"""  
LJ_ioPUT_CAL_CONSTANTS = 400
"""Deprecated constant"""  
LJ_ioGET_CAL_CONSTANTS = 401
"""Deprecated constant"""  
LJ_ioPUT_USER_MEM = 402
"""Deprecated constant"""  
LJ_ioGET_USER_MEM = 403
"""Deprecated constant"""  
LJ_ioPUT_USB_STRINGS = 404
"""Deprecated constant"""  
LJ_ioGET_USB_STRINGS = 405
"""Deprecated constant"""  

