Universal Library Language Interface

The interface to all languages is a set of methods or function calls and enumerations or constants. The interface is nearly identical for each language.

This topic is brief with respect to details of language use and syntax. For more detailed information review the example programs for each language. The example programs, or a link to the example programs for each language, are located in the installation directory, with the exception of Python examples. Example programs for the UL Python API are available for download with the mcculw package on GitHub and PyPI.

Select a link below for more information.

Universal Library for Windows

Function Arguments

Each library function takes a list of arguments and most return an error code. Some functions also return data via their arguments. For example, one of the arguments for cbAIn() is the name of a variable in which the analog input value will be stored. All function arguments that return data are listed in the "Returns:" section of the function description.

Constants

Many functions take arguments that must be set to one of a small number of choices. These choices are all given symbolic constant names. For example, cbLogGetPreferences() takes an argument called TimeFormat that must be set to either TIMEFORMAT_12HOUR or TIMEFORMAT_24HOUR. These constant names are defined and assigned a value in the "header" file for each language. Although it is possible to use the numbers rather than the symbolic constant names, we strongly recommend that you use the names. This will make your programs more readable and more compatible with future versions of the library. The numbers may change in future versions but the symbolic names will always remain the same.

Options Arguments

Some library functions have an argument called Options. The option argument is used to turn on and off various optional features associated with the function. If you set Options = 0, then the function will set all of these options to the default value, or off.

Some options can have an alternative value, such as BACKGROUND and FOREGROUND. If an option can have more than one value, one of the values is designated as the default.

Individual options can be turned on by adding them to the Options argument. For example, Options = BACKGROUND will turn on the "background execution" feature. Options = BACKGROUND+CONTINUOUS will select both the "background execution" and the "continuous execution" feature.

Error Handling

Most library functions return an error code. If no errors occurred during a library call, 0 (or NOERRORS) is returned as the error code; otherwise, one of the codes listed in Error Codes is returned.

If a non-zero error code is returned, you can use cbGetErrMsg() to convert the error code to a specific error message. As an alternative to checking the error code after each function call, you can turn on the library's internal error handling with cbErrHandling().

16-bit Values using a Signed Integer Data Type

When using functions that require 16-bit values, the data is normally in the range 0 to 65,535. However, some programming languages, such as Visual Basic®, only provide signed data types. When using signed integers, reading values above (32,767) can be confusing.

The number (32,767) is equivalent to (0111 1111 1111 1111) binary. The next increment (1000 0000 0000 0000) binary has a decimal value of (–32,768). The maximum value (1111 1111 1111 1111) binary translates to (-1) decimal. Keep this in mind if you are using languages that don't support unsigned integers.

There is additional information on this topic in the Universal Library Function Reference. Also, refer to the documentation supplied with your language compiler.

32-bit Values using a Signed Long Data Type

When using functions that require 32-bit values, the data is normally in the range 0 to 4,294,967,295. However, some programming languages, such as Visual Basic®, only provide signed data types. When using signed integers, reading values above (2,147,483,647) can be confusing. The number (2,147,483,647) is equivalent to (0111 1111 1111 1111 1111 1111 1111 1111) binary. The next increment (1000 0000 0000 0000 0000 0000 0000 0000) binary has a decimal value of (–2,147,483,648). The maximum value (1111 1111 1111 1111 1111 1111 1111 1111) binary translates to (–1) decimal. Keep this in mind if you are using languages that don't support unsigned longs.

Visual Basic

To use the Universal Library with Visual Basic, include the Universal Library declaration file CBW.BAS file as a module in the project, or include it by reference inside Forms which call into the Universal Library. Once UL declarations are added to the project, call the library functions from the Form event handlers. CBW.BAS references CBW32.DLL to call Universal Library functions; this is accomplished with conditional compile statements.

For Visual Basic 6.0 and older, Windows memory buffers cannot be mapped onto arrays. As a consequence, the cbWinArrayToBuf() and cbWinBufToArray() functions must be used to copy data between arrays and Windows buffers.

Example:

Count = 100

MemHandle = cbWinBufAlloc (Count)

cbAInScan (......,MemHandle,...)

For i = 0 To Count

Print DataArray(i)

Next i

cbWinBufFree (MemHandle)

Visual C++

To use the Universal Library with MS Visual C++, include the Universal Library header file CBW.H in the C/C++ program, and add the library file CBW32.LIB to the library modules for linking to the CBW32.DLL.

Universal Library for .NET

.Net languages such as C# and Visual Basic .Net are supported by simply adding a reference to MCCDAQ.DLL to your Visual Studio project. (Visual Studio 2005 or greater is required to run example programs.)

The Microsoft .NET platform provides a framework that allows for the development of Windows applications using a wide range of programming languages such as VB .NET, C#, managed C++, JScript, and any other language that is compliant with the .NET Common Language Runtime (CLR). The CLR is a multi-language execution environment.

The interface to the Universal Library consists of standard "C" functions. These functions are not CLR-compliant. Therefore, the Universal Library for .NET was developed. This library enables the various .NET programming languages to call into the Universal Library.

Note: Universal Library support for .NET requires that the Microsoft .NET Framework 2.0 be installed on the system before you install the Universal Library.

MccDaq namespace

The MccDaq namespace is a naming scheme for grouping types into logical categories of related functionality. The namespace contains the classes and enumerated constants by which UL for .NET applications access the Universal Library data types and functions. The namespace provides an interface that exposes each Universal Library for .NET function as a member of a class with virtually the same parameters set as their UL counterparts.

When you develop a .NET application that uses the Universal Library, you add the MccDaq Namespace as a reference to your project. There are no header files in a .NET project.

MccDaq classes

The Universal Library for .NET consists of a set of classes. For the most part, the methods within each class have a corresponding function in the standard UL. Each UL for .NET method has virtually the same parameter set as their UL counterparts. The MccDaq Namespace contains the following classes:

Class nameDescription
MccBoardProvides access to all of the methods for data acquisition and properties providing board information and configuration for a particular board.
ErrorInfoContains all of the members for storing and reporting error codes and messages. This class also includes error code enumerated constants, which define the error number and associated message that can be returned when you call a method.
MccServiceContains all of the members for calling utility UL functions.
cGlobalConfigContains all of the members for getting global board configuration information.
DataLoggerContains all of the members for reading and converting binary log files.
DaqDeviceManagerContains all of the members for discovering USB, Bluetooth, and Ethernet devices without using InstaCal.
DaqDeviceDescriptor Contains all of the members for examining the descriptor information of a detected device.

The MccDaq namespace also contains the following secondary classes:

Class nameDescription
cBoardConfigContains all of the members for setting and getting board-level configuration.
cCtrConfigContains all of the members for setting and getting the counter-level configuration of a board.
cDIOConfigContains all of the members for getting the digital configuration of a board.
cExpansionConfigContains all of the members for setting and getting expansion board configuration.

These secondary classes include methods that are accessible from properties of the MccBoard class.

The MccDaq assembly allows you to design Common Language Specification (CLS) –compliant programs. A CLS-compliant program contains functions that can be called from any existing or future language developed for the Microsoft .NET Framework. Using CLS-compliant data types ensures future compatibility.

Board Number

In a .NET application, you can program multiple boards by creating an MccBoard class for each board installed.

Board0=new MccBoard(0)

Board1=new MccBoard(1)

Board2=new MccBoard(2)

The board number may be passed into the MccBoard class, which eliminates the need to include the board number as a parameter to the board methods.

MccDaq Enumerated types

Instead of using constants such as BIP5VOLTS, the Universal Library for .NET uses enumerated types. An enumerated type take settings such as range types, scan options or digital port numbers, and puts them into logical groups.

When programming inside of Visual Studio .NET, the types that are available for a particular enumerated value display automatically when you type code. The example below lists MccDaq.ScanOptions enumerated values.

scanoptions enum values

Parameter data types

Many of the Universal Library for .NET methods are overloaded to provide for signed or unsigned data types as parameters. The AConvertData() function is shown below using both signed and unsigned data types.

VB .NET

Public Function AConvertData(ByVal numPoints As Integer, ByRef adData As Short, ByRef chanTags As Short) As MccDaq.ErrorInfo
Member of MccDaq.MccBoard

 

Public Function AConvertData(ByVal numPoints As Integer, ByRef adData As System.UInt16, ByRef chanTags As System.UInt16) As MccDaq.ErrorInfo
Member of MccDaq.MccBoard

C# .NET

public MccDaq.ErrorInfo AConvertData (System.Int32 numPoints, System.Int16 adData, System.Int16 chanTags)
Member of MccDaq.MccBoard

 

public MccDaq.ErrorInfo AConvertData(System.Int32 numPoints, System.UInt16 adData, System.UInt16 chanTags)
Member of MccDaq.MccBoard

For most data acquisition applications, unsigned data values are easier to manage. However, since Visual Basic (version 6 and earlier) does not support unsigned data types, it may be easier to port these programs to .NET if the signed (Int16) data types are used for the function parameters. For additional information on using signed data types, refer to 16-bit values using a signed integer data type.

The short (Int16) data type is CLS-compliant, supported in VB, and will be included in any future .NET language developed for the .NET Framework. Using CLS-compliant data types ensures future compatibility. Unsigned data types (UInt16) are not CLS-compliant, but are still supported by various .NET languages, such as C#.

Error Handling

The return value for a UL for .NET method is an ErrorInfo() object that contains the number of the error that occurred as well as associated error message.

The example below shows how to perform error checking within a .NET aplication:

ULStat=Board0.AIn(Channel, Range, DataValue)

'check the numeric value of ULStat

If Not ULStat.Value = ErrorInfo.ErrorCode.NoErrors Then

'if there was an error, then display the error message

MsgBox ULStat.Message

EndIf

Configuring a UL for .NET Project

Programming the Universal Library API is available through the various languages supported by the Microsoft .NET Framework. All .NET applications access the Universal Library (CBW32.DLL and CBW64.DLL) through the MccDaq .NET assembly (MCCDAQ.DLL). The MccDaq assembly provides an interface that exposes each Universal Library function that is callable from the .NET language.

The Universal Library for .NET is designed to provide the same "look and feel" as the Universal Library for Windows. This design makes it easier to port over existing data acquisition programs, and minimizes the learning curve for programmers familiar with the Universal Library API.

In the Universal Library for .NET, each function is exposed as a class method with virtually the same parameter set as their UL counterparts.

Referencing the MccDaq Namespace in a .NET Project

In a .NET application, there are no header files to include in your project. You define methods and constants by adding the MccDaq assembly, or Namespace, as a reference to your project. You access UL for .NET methods through a class that has the Universal Library as a member.

To add the MccDaq Namespace as a reference in a Visual Studio .NET project:

  1. Start a new Visual Basic or C# project in Visual Studio .NET.
  2. From the Visual Studio .NET Solution Explorer window, right-click on References and select Add Reference.
  3. Solution Explorer Add Reference option selected
  4. The Add Reference window appears.
  1. From the .NET tab, select the MccDaq option from the displayed list of .NET assemblies and click on the OK button.
  2. Add Reference dialog
  3. MccDaq appears under the References folder in the Solution Explorer window.
  4. Solution Explorer dialog showing MccDaq reference added

The MccDaq Namespace is now referenced by your Visual Studio .NET project.

Python

The Measurement Computing Universal Library Python API for Windows is a wrapper around Universal Library functions, and can be imported into any integrated development environment (IDE) that supports Python, such as Eclipse (Neon and later) and Visual Studio (2015 and later). The UL for Python example programs can be run with Measurement Computing USB, Bluetooth, or Ethernet hardware. For specific mcculw package requirements, refer to the download location on GitHub. By default, these examples use discovery, which is compatible with these products. Most other MCC hardware is supported using Instacal and making a slight modification to the examples. Refer to the Notes on Python Support for more information.

The main difference between the Python API and the UL for most other languages is how lists and arrays are used, how values are returned, and how errors are handled.

Lists and Arrays

Python uses lists instead of arrays as the standard container to group multiple items together. Unlike arrays, lists can contain items of different data types, although it is more common for list items to have the same type. C arrays can be used when speed is required to ensure that data is handled efficiently. Lists are created by simple assignment: my_list = []. Multiple items are separated by commas.

Lists are dynamically sized, so users can add or remove items without defining a size. When performance is not a concern, a list can be passed directly to a function and Python will convert it to a c-type array. Examples of functions that do this include a_load_queue, d_out_array, and daq_in_scan (for config items such as chan_list, gain_list, and so on, but not the data).

When scanning a pointer to a C array, memhandle is created with win_buf_alloc() and passed to a_in_scan() and other scanning methods. The pointer returned from win_buf_alloc() can be cast to a ctypes array at any point. The cast works in the same way as a c-style cast, in that the pointer will be valid until win_buf_free() is called on memhandle:

array = ctypes.cast(memhandle, ctypes.POINTER(ctypes.c_ushort))

take care to call win_buf_free(), typically in the finally block of a try-finally clause, to prevent memory leaks.

Some functions require the user to allocate a c-type array, such as win_buf_to_array(). To create a c-type array of unsigned short values with a size num_points, the user can call:

my_array = (ctypes.c_ushort * num_points)()

Arrays created in this way are not dynamically sized, but they are much faster and do not require a copy when used with a C library like UL.

Users may want to import the Python numpy library as it provides a multidimensional array object and tools for working with arrays. Numpy is commonly used as an alternative to directly using c-type arrays.

View the example program ULAI02.py with a text editor for an example of using arrays in Python with the UL library.

Multiple return values

Python doesn't use pointers for parameters, unlike C and other languages. In Python, returning multiple values is the standard way of providing more than one result from a single function call. Refer to the following example for a pulse output operation in C/C++ using pointers, and in Python using multiple return values:

Pulse output example showing multiple return values/pointers

C/C++

int BoardNum = 0;
int Chan = 0;
double Frequency = 100, DutyCycle = 0.5, InitialDelay = 0;


ULStat = cbPulseOutStart(

BoardNum, Chan, &Frequency, &DutyCycle, 0, &InitialDelay, IDLE_LOW, 0);

displayActualValues(Frequency, DutyCycle, InitialDelay)


Python

try:

board_num = 0
timer_num = 0
freq = 100
duty_cycle = 0.5
initial_delay = 0


act_freq, act_duty_cycle, act_initial_delay = ul.pulse_out_start(

board_num, timer_num, freq, duty_cycle,
initial_delay, 0, TimerIdleState.IDLE_LOW, 0)

display_actual_values(act_freq, act_duty_cycle, act_initial_delay)

except ULError as e:

util.show_ul_error(e)

Error Handling

Instead of returning an error code when an error occurs, Python throws an exception that contains the error code and error message. Python assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This approach is both clean and fast, and characterized by many try and except statements. We recommend that users program the try/except construct around each function to manage exceptions.

Note: Some of the Python API functions return a warning. For example, the pre-trigger functions will return a warning rather than throwing an exception when there isn't enough data. Likewise, the thermocouple input scan method will return a warning rather than an exception when the value is out of range or when an open thermocouple is detected.

Refer to the following code for an analog input example that shows how error handling is performed in C/C++ and in Python.

Basic analog input example

C/C++

int BoardNum = 0;
int UDStat = 0;
int Chan;
int Gain = BIP5VOLTS;
WORD DataValue = 0;


UDStat = cbAIn(BoardNum, Chan, Gain, &Value, Options);

if(UDStat) {

printf("An error occurred, Error Code = %d", UDStat);

} else {

printf("%u", Value);

}

Python

board_num = 0
channel = 0
ai_range = ULRange.BIP5VOLTS


try:

# Get a value from the device
value = ul.a_in(board_num, channel, ai_range)
# Display the value
print("Value = " + str(value))

except ULError as e:

util.show_ul_error(e)

Differences between the UL, UL for .NET, and Python

The table below lists the main differences between the Universal Library, Universal Library for .NET, and Python.

 Universal LibraryUniversal Library for .NETPython
PlatformStandard Windows platformThe Microsoft .NET framework must be installed on development and distribution PCs.Standard Windows platform (Windows 10/8/7 and later), the library can be imported into any Python-supporting IDE such as Eclipse and Visual Studio.
Board NumberThe board number is included as a parameter to the board functions.An MccBoard class is created for each board installed, and the board number is passed to that board class.The board number is included as a parameter to the board functions.
FunctionsSet of function calls defined in a header.Set of methods in the MccBoard and MccService classes. To access a method, instantiate a UL for .NET class and call the appropriate method using that class.Set of function calls defined in a Python module.
ConstantsConstants are defined and assigned a value in the header file.Constants are defined as enumerated types.Constants are defined as enumerated types.
Error handlingAn error code is returned.An ErrorInfo object that contains the error number and message is returned.An exception is thrown which contains the error code and error message. Some functions return a warning if the error can be ignored.

Example

The following example demonstrates how to call the AIn() method from within a 32-bit UL application, .NET application, and Python application:

VB program using the ULVB program using UL for .NETPython for Windows program
Dim Board As IntegerDim Board0 as MccBoard
Board0 = new MccDaq.MccBoard(0)

board_num = 0
Dim Channel As IntegerDim Channel As Integerchannel = 0
Dim Range As IntegerDim Range As MccDaq.RangeULRange.BIP5VOLTS
Dim ULStat As IntegerDim ULStat As ErrorInfo 
Dim DataValue As ShortDim DataValue As UInt16 
 
 
Board = 0
Channel = 0
Range = BIP5VOLTS;
ULStat = cbAIn(Board, Channel,
Range, DataValue)
Channel = 0
Range = Range.BIP5VOLTS;
ULStat = Board0.AIn(Channel, Range, DataValue)
try:
     # Get a value from the device
     value = ul.a_in(board_num, channel, ai_range)
     # Display the value
     print("Value = " + str(value))
except ULError as e:
     util.show_ul_error(e)