DALI User Manual: Windows Client

Introduction
Installation
Your First dali_w32.lib Console Application
CDali API
What is a spread interface?
Other Functions
Your First DALI MFC Application - Using Interfaces and the DALI MFC Control Library
How To Add ActiveX Controls To Your Visual C++ 6.0 Project

Introduction

The DALI Win32 software package provides libraries and sample code that allow a developer to quickly and easily create programs that communicate with a DALI server.

At the lowest level, DALI communication is done via the open, widely used SOAP protocol. Writing software to use this protocol is not a trivial task, which is why we provide three libraries that provide increasing layers of abstraction.

The first library we provide is dalic.lib, a C-language development library. This library handles all the networking code, and wraps all supported SOAP calls into an easy-to-use C-style API.

The second library we provide is dali_w32.lib. This is a C++ library built from dalic.lib. It abstracts much of the programmatic complexity of dalic.lib in a class named CDali. This class supports both MFC and non-MFC applications.

The third library is a collection of custom MFC and ActiveX controls. When these controls are registered with a CDali class, they automatically handle everything necessary to continuously monitor and interact with a DALI server.

Installation

There is no installer needed to use the DALI development libraries; you simply include them in your project as you would any library. To make this task easier, you may copy the header (.h) files and library (.lib) files into your compiler's default directories.

For Visual Studio 6.0, this would be:

Header files: C:\Program Files\Microsoft Visual Studio\VC98\Include
Library files: C:\Program Files\Microsoft Visual Studio\VC98\Lib

Alternatively, you may run the copylibs.bat script to automate this process.

Install the ActiveX controls by running the installax.bat script.

Your First dali_w32.lib Application

This tutorial assumes that you have copied dalic.h/dali_w32.h into your include directory, and dalic.lib/dali_w32.lib into your lib directory. It also assumes that you have a DALI server running that you can connect to.

To begin, let's make a simple console application. Open Visual C++ 6.0. Select "File->New..." from the menu. Choose "Win32 Console Application" and name the project "daliconsole". Press the "OK" button.

At the project setup screen, create an empty project and select "Finish", then "OK".

Press Alt-F7 to bring up the Project Settings dialog. Under the General tab, make sure that "Use MFC in a Shared DLL" is selected.

Under the Link tab, make sure the linker includes "dalic.lib dali_w32.lib ws2_32.lib".

Now, go to "File->New..." and create a new C++ source file named daliconsole.cpp. Here is the code to place in the file:

#include 
#include "dali_w32.h"

int main(int argc, char ** argv)
{
    CDali dali;

    int rc = dali.SetServer("192.168.1.221", 8444, 0);
    if (rc)
    {
        printf("rc = %d\n", rc);
        return rc;
    }

    DALIADSETTINGS adsettings;
    adsettings.gain = 1;
    adsettings.polarity = 0;
    adsettings.range = 1;

    dali.PerformDAConvert(0, 0, 4095);

    int sample;
    dali.PerformADSample(0, 0, &adsettings, &sample);

    printf("Sample: %d\n", sample);
    getchar();

    return 0;
}

Replace the server IP and port with those matching your own running DALI server. For simplicity, this tutorial assumes that the DALI server has one board (board 0) that is capable of D/A and A/D operations. This kind of assumption is not good practice, and normal DALI applications should first interrogate the server for a list of boards and their capabilities. Examples for that kind of operation can be found in the sample code section.

For now, you can build this code (F7). Ignore warnings about the default libraries. They won't affect the code. These warnings appear because you are using the MFC DLL in a console application, and can be removed by following the warning instructions (adding appropriate /nodefaultlib statements to the linker options.)

Press F5 to run your application in debug mode. The board attached to the DALI server will perform a D/A conversion with 4095 and return a single A/D sample.

CDali API

All functions listed below are members of the CDali class.

Unless otherwise stated, the return values are error codes. A return value of 0 denotes success; non-zero means failure (compare value to defined error codes, or use GetLastErrorString).

CDali(LPCTSTR name, UINT port, LPCTSTR userid, LPCTSTR passwd, BOOL secure_http)
This is the constructor for the CDali object. If no arguments are passed, the constructor will only allocate memory for the object, but will not be configured for connection. If parameters are passed, the CDali constructor will call SetServer with the appropriate arguments.
Returns: The constructor has no return value.
LPCTSTR name
The IP address or domain name of the server to use (e.g. "192.168.1.1", "daliserver.myhost.com", etc.)
UINT port
The TCP/IP port to connect to on the DALI server. The DALI default port is 8444 for unencrypted connections, and 8443 for SSL encryption.
LPCTSTR username
The username to use for non-public DALI operations.
LPCTSTR passwd
The password to use for non-public DALI operations.
BOOL secure_http
secure_http - this flag determines if the CDali class will use OpenSSL for secure http transactions.
UINT SetServer(LPCTSTR name, UINT port, BOOL secure_http)
This function takes in the network address and port of the DALI server to connect to and configures the CDali object accordingly.
Returns: Non-zero on error.
LPCTSTR name
he IP address or domain name of the server to use (e.g. "192.168.1.1", "daliserver.myhost.com", etc.)
UINT port
The TCP/IP port to connect to on the DALI server. The DALI default port is 8444 for unencrypted connections, and 8443 for SSL encryption.
BOOL secure_http
secure_http - this flag determines if the CDali class will use OpenSSL for secure http transactions.
UINT GetServer(LPTSTR name, UINT max_name, UINT* port)
Returns: Non-zero on error.
LPCTSTR name
Pointer to a buffer which will accept the server associated with the CDali object.
UINT port
The size of the name buffer. This is the max number of characters the function will copy into the name buffer.
BOOL secure_http
Pointer to an unsigned int variable that will accept the port associated with the CDali object.
UINT GetLocalIP(LPTSTR ip)
This function returns the ip address of the local machine.
Returns: Non-zero on error.
LPCTSTR ip
Pointer to a buffer that will take the local IP address. This buffer must be at least 16 bytes wide.
UINT SetUserID(LPCTSTR userid)
Set the username for non-public DALI operations. Once called, the same username will be used for all subsequent DALI functions. If you wish to clear the username, call SetUserID("");
Returns: Non-zero on error.
LPCTSTR userid
A null-terminated string that will become the new username.
UINT GetUserID(LPCTSTR userid, UINT max_len)
Get the username for non-public DALI operations.
Returns: Non-zero on error.
LPCTSTR userid
A null-terminated string that will hold the username.
UINT max_len
The size of the userid buffer.
UINT SetPassword(LPCTSTR password)
Set the password for non-public DALI operations. Once called, the same password will be used for all subsequent DALI functions. If you wish to clear the password, call SetPassword("");
Returns: Non-zero on error.
LPCTSTR password
A null-terminated string that will become the new password.
UINT GetPassword(LPCTSTR password, UINT max_len)
Get the password for non-public DALI operations.
Returns: Non-zero on error.
LPCTSTR password
A null-terminated string that will hold the password.
UINT max_len
The size of the password buffer.
LPCTSTR GetLastErrorString()
This function returns a pointer to the English explanation of the last error that occurred. The returned buffer should be used for read-only purposes, and the contents are not guaranteed to stay constant. It is a static buffer that is modified whenever an error occurs.
UINT GetBoardHandle(LPCTSTR board_name, UINT* board)
Returns the index of a specifically named board. This function must be called to associate a board handle with a board string. You may then use the returned handle in all subsequent CDali functions which require one. A handle value is not valid unless is was returned by this function. Requesting the handle of already requested board will return the same value that was previously returned.
LPCTSTR board_name
A null-terminated character buffer holding the name of the desired board.
UINT *board
Pointer to an unsigned int that will take the index of the specified board.
UINT GetNumBoards(UINT* numboards)
This function returns the number of active boards recognized by the associated DALI server.
UINT *numboards
Pointer to an unsigned int that will accept the number of boards listed by the DALI server.
UINT GetBoardInfo(UINT boardnum, DALIBOARD* boardinfo)
This function fills a DALIBOARD structure with information associated with a board.
UINT boardnum
Specifies which board to get information of.
DALIBOARD *boardinfo
Pointer to a DALIBOARD structure that will have its contents filled with information on the specified board.
UINT GetNumChannels(UINT board, UINT *numchans)
This function returns the number of channels associated with a particular board.
UINT board
Which board to interrogate.
UINT *numchans
Pointer to an unsigned int that takes the number of channels in the specified board.
UINT GetChannelInfos(UINT board, UINT chan, DALICHANNEL *chaninfo)
This function queries the DALI server about a board/channel and fills in the DALICHANNEL structure accordingly.
UINT board
Which board to interrogate.
UINT chan
Which channel to interrogate.
DALICHANNEL *chaninfo
Pointer to a DALICHANNEL struct that receives information regarding the channel.
UINT RegisterInterface(DALI_INTERFACE_SETTINGS* dali_int, CWnd* user_cwnd, UINT* int_handle)
UINT RegisterInterface(DALI_INTERFACE_SETTINGS* dali_int, CWnd** user_cwnd, UINT* int_handle)
UINT RegisterInterface(DALI_INTERFACE_SETTINGS* dali_int, DALI_CALLBACKFUNC user_func, UINT* int_handle)
UINT UnRegisterInterface(UINT handle);

When creating an MFC application, you will most likely have several controls all constantly querying and displaying different values from the DALI server. Under normal circumstances, this would be a complicated task of managing threads or handling timer events.

The CDali class presents an "interface" paradigm, allowing controls to plug into the CDali class with a single function call. The CDali class will handle all threading and messaging behind the scenes. Our library of custom MFC and ActiveX controls know how to interact with the CDali object to constantly update and send messages back and forth.

You can also easily write your own CWnd-derived controls that communicate with the CDali class through an interface, as well as hook standard functions to be triggered on events.

Anytime you register an interface, you pass in a pointer to a DALI_INTERFACE_SETTINGS struct. These are the input members:

UINT dali_board
This is similar to the 'board' parameter of standard CDali member functions. It specifies the DALI board that will be used for the interface.
UINT operation_type
This is the operation to be performed by the interface. You should pick from one of the following predefined commands:
DALI_CMD_AD_SAMPLE         
DALI_CMD_AD_SCAN           
DALI_CMD_DA_CONVERT        
DALI_CMD_DA_READBACK       
DALI_CMD_DIO_IN_BYTE       
DALI_CMD_DIO_IN_BIT        
DALI_CMD_DIO_READBACK_BYTE 
DALI_CMD_DIO_READBACK_BIT  
DALI_CMD_DIO_OUT_BYTE      
DALI_CMD_DIO_OUT_BIT      
DALI_CMD_AD_SCAN_SPREAD            
DALI_CMD_DIO_IN_BYTE_SPREAD        
DALI_CMD_DIO_READBACK_BYTE_SPREAD
(The spread commands will be discussed later.)
UINT low_channel_port
This is the 'low channel' or 'port' parameter, depending on which operation you are performing.
UINT high_channel_bit
This is the 'high channel' or 'bit' parameter, depending on which operation you are performing.
UINT ms_interval
This is the desired interval between event calls. This parameter is only valid on input-based interfaces (A/D, DIO Input, etc.) The actual interval will be greater than or equal to this number; it depends on a multitude of factors that affect how quickly calls can be made to the DALI server.
DALIADSETTINGS adsettings
A typical DALIADSETTINGS struct that is used to configure A/D interfaces.
CWnd *user_cwnd
This is either a pointer to a CWnd object, or an array of pointers to CWnd objects. If an array of pointers is detected, the CDali object assumes you are registering a spread-based interface, which will be explained shortly.
DALI_CALLBACKFUNC user_func
This is a pointer to a DALI_CALLBACKFUNC function that will be called whenever an interface event is generated.
UINT int_handle
This is a number unique to the registered interface. It is used to unregister (stop) an interface via UnRegisterInterface.

What is a spread interface?

Certain data acquision operations return multiple values (e.g. A/D Scan.) Unfortunately, most controls can only represent one value. For this reason, it is nice to split a multi-valued operation among many controls (e.g. split a 16-channel A/D scan among 16 progress bars.)

For this purpose the DALI_REGISTER_SPREAD macro is used:

DALI_REGISTER_SPREAD(daliclass, dis, name, size, handle)
daliclass
the name of the CDali object
dis
The DALI_INTERFACE_SETTINGS struct to use. Be sure that the operation_type is a valid 'spread' command.
name
The name of an array of pointers to the desired interface controls.
size
The size, in elements, of the array of pointers to the desired interface controls.
handle
An unsigned int variable that will take the interface handle.

Other Functions

These are all pretty straightforward; they're just wrappers for the associated dalic functions applied to the specified 'board'.

UINT PerformADSample(UINT board, UINT channel, DALIADSETTINGS* adsettings, int* value)
UINT PerformADScan(UINT board, UINT low_channel, UINT high_channel, DALIADSETTINGS* adsettings, int* values)
UINT PerformADSampleMulti(UINT board, UINT channel, UINT count, float hz, DALIADSETTINGS* adsettings, int* value)
UINT PerformADScanMulti(UINT board, UINT low_channel, UINT high_channel, UINT count, float hz, DALIADSETTINGS* adsettings, int* values)
UINT PerformDAConvert(UINT board, UINT channel, int value)
UINT PerformDAReadBack(UINT board, UINT channel, int* value)
UINT PerformDIOOutputByte(UINT board, UINT port, int value)
UINT PerformDIOOutputBit(UINT board, UINT port, UINT bit, int value)
UINT PerformDIOInputByte(UINT board, UINT port, int* value)
UINT PerformDIOInputBit(UINT board, UINT port, UINT bit, int* value)
UINT PerformDIOReadBackByte(UINT board, UINT port, int* value)
UINT PerformDIOReadBackBit(UINT board, UINT port, UINT bit, int* value)

See the DALI C/C++ Client documentation in this manual for more information on these parameters.

Your First DALI MFC Application Using Interfaces and the DALI MFC Control Library

This tutorial will walk you through all the steps necessary to create a fully automated MFC application that will interact with a DALI server. If you'd like to open the finished project, you can find it in the samples directory distributed with the DALI server as DALIMFCTest.zip".

The first thing to do is create a new MFC application. Open Visual C++ 6.0, select File->New..., highlight "MFC AppWizard (exe)", enter the desired path in "Location:" and enter "DALIMFCTest" in the "Project Name:" field.

Press the "OK" button. On the next screen, make sure "Dialog Based" is marked, then click on the "Finish" button. Visual Studio will now generate all initial project and source files for you.

Now go to the project settings by pressing Alt+F7. Go to the Link tab and enter dalic.lib dali_w32.lib dalictrl.lib ws2_32.lib into the Object/Library modules box:

This will import all of the DALI libraries, as well as the Winsock library, into your project.

Go to the main dialog resource and remove the TODO label in the middle of the dialog (click on it with the mouse and press the delete key.)

Select the progress control button from the controls box and paint a progress bar onto the dialog. Right click on the bar and select "Properties". Go the Styles tab and enable Vertical and Smooth:

Now resize the progress bar to a more suitable vertical shape. While it is highlighted, press Ctrl+C to copy the control to the clipboard, and use Ctrl+V to lay down 3 more identical copies - to make four progress bars in a row.

Lay down a slider control, a button control, and a radio control. Change the labels on the button and the radio control to be blank. Your dialog box should now look something like this:

Now we're going to insert the code which will connect these controls to the DALI server. The first thing we have to do is include the dalictrl.h file in our dialog's header file (DALIMFCTestDlg.h) at line 10. The result should look something like this:

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "dalictrl.h"

Now go to the DALIMFCTestDlg class declaration in DALIMFCTestDlg.h, line 19. Add the following lines:

class CDALIMFCTestDlg : public CDialog
{
// Construction
public:
    CDALIMFCTestDlg(CWnd* pParent = NULL);	// standard constructor

    CDali dali;

    CDALIProgressCtrl m_progress[4];
    CDALISliderCtrl m_slider;
    CDALIButtonCtrl m_button;
    CDALICheckBoxCtrl m_radio;

    void HandleError(int rc, char* str);

// Dialog Data
        //{{AFX_DATA(CDALIMFCTestDlg)

The CDALIxxx classes are defined in the DALI control library and contain the logic necessary to be used as an interface. The CDali object will be the main interface to the DALI server. The HandleError function will provide us with simple error codehandling.

Now open the dialog source file DALIMFCTestDlg.cpp. Go to the DoDataExchange member function and add these lines:

    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CDALIMFCTestDlg)
        // NOTE: the ClassWizard will add DDX and DDV calls here
    //}}AFX_DATA_MAP

    DDX_Array(IDC_PROGRESS1, m_progress, 4);
    DDX_Control(pDX, IDC_SLIDER1, m_slider);
    DDX_Control(pDX, IDC_BUTTON1, m_button);
    DDX_Control(pDX, IDC_RADIO1, m_radio);

These lines will attach the previously defined classes to the controls we drew in the resource editor. The DDX_Array macro is provided by the dali_w32.h header file to allow quick mapping of an entire control array.

We will also want to include our HandleError function to catch error codes. You can place this function at the end of the DALIMFCTestDlg.cpp file:


void CDALIMFCTestDlg::HandleError(int rc, char* str)
{
    if (rc)	
    {
        char buffer[1024];
        sprintf(buffer, "%s Error [%d]: %s\n", str, rc, dali.GetLastErrorString());
        MessageBox(buffer, "DALI Error"); 
        exit(rc);
    }
}

Now we add the heart of the DALI code into the OnInitDialog member function. This code initializes the controls and starts the interfaces between the CDali object and the control objects. When the initialization concludes, you will be able to monitor 4 A/D channels, change one D/A channel and toggle/view a DIO channel. This code will be inserted at line 124 of DALIMFCTestDlg.cpp:


// TODO: Add extra initialization here
	
for (int i = 0; i < 4; i++) m_progress[i].SetRange32(-32767, 32767);
m_slider.SetRange(0, 4095);
	
UINT bhandle, ihandle;

HandleError(dali.SetServer("127.0.0.1", 8444, 0), "dali.SetServer()");
HandleError(dali.GetBoardHandle("demo", &bhandle), "dali.GetBoardHandle()");

DALI_INTERFACE_SETTINGS dis;

DIS_OPER(dis, bhandle, DALI_CMD_AD_SCAN_SPREAD, 100);
DIS_CHAN(dis, 0, 3);
DIS_ADSET(dis, 10, DALI_BIPOLAR, 1);
	
//Register A/D
DALI_REGISTER_SPREAD(dali, dis, m_progress, 4, ihandle);

//Register D/A
DIS_OPER(dis, bhandle, DALI_CMD_DA_CONVERT, 100);
HandleError(dali.RegisterInterface(&dis, &m_slider, &ihandle), "dali.RegisterInterface(DA)");

//Register DIO out
DIS_OPER(dis, bhandle, DALI_CMD_DIO_OUT_BIT, 100);
DIS_CHAN(dis, 0, 0);
HandleError(dali.RegisterInterface(&dis, &m_button, &ihandle), "dali.RegisterInterface(DIO Out)");

//Register DIO in
DIS_OPER(dis, bhandle, DALI_CMD_DIO_IN_BIT, 100);
HandleError(dali.RegisterInterface(&dis, &m_radio, &ihandle), "dali.RegisterInterface(DIO Out)");
	
return TRUE;  // return TRUE  unless you set the focus to a control

That's all there is - in total about 30-40 extra lines of code. You will want to change the IP of the destination server if you are not running a DALI Server on your local machine. You can always connect to dali.diamondsystems.com as a default.

Now build the project with F7. The DALI libraries were built with the release version of MFC, so you may receive a warning when building a debug-version MFC application. It is ok to ignore this warning. Press F5 to run!

How To Add ActiveX Controls To Your Visual C++ 6.0 Project

For this example you should create a new MFC application (using the MFC AppWizard) and create a default dialog application.

Select from the menu, "Project->Add To Project->Components and Controls..."

You will get this window:

Open "Registered ActiveX Controls" and locate the ActiveX control you wish to include. Here I will include DALIDigitalBarGraph:

Now click on "Insert" and choose "OK", you will get this window:

Click on "OK" and it will insert the new .h and .cpp file into your project (in this case, dalidigitalbargraph):

You will now notice that in your dialog box resource editor there is a new component. Painting this new component on the dialog box will create DALIDigitalBarGraph objects.

The DALIDigitalBarGraph component will now interact with the resource editor and classwizard like any standard MFC common control.