Getting started

Introduction

The Riegl LMS Scanner Library is a set of functions that will help you The library is packaged as COM objects, that are located in files named scannermod.dll and scancnfmod.dll. Once the library has been registered into your system, your compiler will be able to read its type library and generate the necessary interfaces. You then call the functions as if they were native functions in your programming environment (e.g Visual C++, Delphi, ...). Since the library is coded in COM technology you even will be able to create multiple instances of interfaces, as you would like when interfacing to more than one scanner at a time or when reading data files at the same time.

The library is running in a separate thread asynchronously to your application. This will ease your interface coding, since you almost never will be blocked when you call any of the interface functions, thereby avoiding the annoying hourglass cursor. To this end the library maintains an internal data buffer for storage of the scanner data, and controls access to it via a semaphore mechanism. The windows message passing system is used to inform you of the relevant events, such as start of a scan, receipt of a single scan-line or end of a scan.
A logging facility also is built into the library. You can use this feature to create files that will store any data the scanning unit is able to deliver, even if there is no interface function to directly access it. Storing the data in this native format will allow to use an even more improved version of the library without compromising compatibility.

Geometry data can be retrieved in spherical or cartesian coordinates. You choose the amount of information you need to get in a single call. You may specify to get any number from a single point up to an entire scan per call. The same is true for intensity (reflectivity) RGB (true color) or time data.

Error situations (such as end of file, wrong file or scanner types, ...) also are signaled via the windows message loop. The library even will give you a plain text message string, that can be used for display in your interface. This message string automatically will be translated to the language that is installed on your system. (currently english and german available).

Prerequisites

The library is able to interface to the scanner data port either through a PC parallel port or a TCP/IP socket using the IB90-ETH box / built in TCP/IP. On Windows NT/2000 interfacing is via device driver RiPort, resulting in very low processor overhead because of the use of interrupts. On Windows95/98 there is no device driver available, resulting in the need to constantly poll the port, thereby incuring high processor load. On Windows 95/98 in principle it is possible to use even an simple bidirectional (not ECP capable) port. This interface however is not recommended at all since only very low data transfer rates are obtainable.
Windows95/98 also needs a further precaution since none of the resource sharing functions for the parallel port are in use. (A simultaneous print attempt might crash the system.)

Installation

Installation of RiSCANLIB comes in several flavours. First you need to find out how you intend to use it. These are just some typical usage scenarios. In each case you will need to do the following steps.
  1. Find out which files to copy to the target computer.
  2. Find out if you need to install licenses for the copied files.

Developer Machine

Most likely the first thing you will do, is install the developer version onto your machine. As you are reading this, most likely you already have installed the library, but might yet have skipped the licensing step. You may make up for this whenever you like, using the LicenseManager utility that can be found in the support group of the Riegl LMS programs under the Start menue.
Launch the License Manager and locate the "Products of all users". You should find there the scancnfmod and scannermod entries. Select one entry, and use the respective button to add your license code(s).
You may add as many licenses as you need. Licenses are usually bound to a specific scanner, so installing all the licenses you have will enable to communicate with any of these scanners.
If you are just using the library for reading 3dd files, you do not need to apply any licenses, but you cannot use any of the online functions of the library.

Target Machine

The scannermod.dll and scancnfmod.dll (refsrchmod.dll) are shipped as self registering COM servers. Therefore you need to copy the files to a central place on the system. We strongly recommend to copy the files to the %SYSTEM%\system32 directory, where %SYSTEM% is a location dependent environment variable that usually evaluates to WINDOWS or WINNT or similar. Your favorite install program generator tool will know how to handle this correctly. Please avoid copying the files into an application subdirectory of your own, since this effectively will defeat the versioning capability of the dll's and may render them unusable.
Next you need to register with the COM subsystem of windows. Depending on your needs you may use the windows regsvr32.exe utility, or the respective commands of your install script language. You even might code this yourself, given the code snippet that follows.
But please do not run this code on every startup of your application. This code is intended to run once during the installation process.
Finally if you intend to use the online capabilities of the library, you will need to install the licenses. You might instruct your useres to use the Riegl LMS LicenseManager or if you want to make this process more comfortable for them, you can use a special function, that is exported from the DLL to this end. Also integrating this step into your setup avoids the necessity to manually apply the same code twice, once to scannermod and once to scancnfmod.
By the way: The DLL files are mostly independent of each other, therefore you will need to register and license them separately. This also implies, that you are safe to copy only the scannermod.dll file if you need only the "read 3dd files" capability.
Do it manually. (discouraged) :
open a command prompt window, copy the DLL files to the intended dircetory, and in this dircetory specify
regsvr32.exe scannermod.dll.
or
regsvr32.exe scancnfmod.dll.
(Do this step for all DLL's.)
You should see a messages box, confirming registration has succeeded. Depending on your intended use you still might need to apply a valid license code. Doing this manually is tedious, but possible. Open regedit, and under
HKEY_LOCAL_MACHINE\Software
create a subkey named
Riegl_LMS\scannermod\Lic.
Under this key create a value named Val of binary type. Enter the 16 byte license key omitting the hyphens.
Add additional licences as values named Val1, Val2, ... , but be careful not create "holes". Alternatively you can use the LicenseManager utility, that will do the latter job.
Use your setup script (highly recommended):
Copy all DLL files to %SYSTEM%\system32 directory.
Register all DLL files as COM objects.
Use the capability of your setup script to write the licenses into the registry keys, that have been described under "Do it manually".
Using Windows API calls to install the library:
The following code snippet shows how to register the COM server and apply the license key:

Run this code only once during installation!
typedef  HRESULT (STDAPICALLTYPE *PSTDAPI_VOID)(void);
typedef  HRESULT (STDAPICALLTYPE *PSTDAPI_PBYTE_UINT)(BYTE*, UINT);
PSTDAPI_VOID pDllRegisterServer;
PSTDAPI_PBYTE_UINT pDllSetLicense;
HMODULE hModule;
// put your license key here
BYTE rgLic = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

hModule = ::LoadLibrary(_T("C:\\scannermod\\scannermod.dll"));
if (NULL != hModule) {
	pDllRegisterServer = (PSTDAPI_VOID)::GetProcAddress(hModule, _T("DllRegisterServer"));
	if (NULL != pDllRegisterServer) {
		if (S_OK == (pDllRegisterServer)()) {
			pDllSetLicense = (PSTDAPI_PBYTE_UINT)::GetProcAddress(hModule, _T("DllSetLicense"));
			if (NULL != pDllSetLicense) {
			    // you may call the next function multiple times to add more licenses
			    // this example adds only one license
			    if (S_OK == (pDllSetLicense)(rgLic, 16)) {
			        // registration and licensing ok
				    // do something with this information
			    }
			}
		}
	}
	::FreeLibrary(hModule);
}

		

Programming

Overview

The library basically consists of two modules, one for configuration, and one for data retrieval (measurement). These modules are realized as COM objects, packaged as in process servers, each of which lives in a DLL. COM objects as such are never accessible by a pointer that will give you control over its memory, but instead you access the object by means of one (ore more) of its interfaces.
One of the main objectives of the library is, to decouple application code from hard coded file formats and from scanner hardware details. This might be compared to a high level printer driver which does not (need not) know much about the actual hardware. Ideally, once the application is in place you simply would need to copy the newest DLL code onto the client PC to obtain access to more advanced scanner hardware.
As said above, you access the library by means of some of its interfaces. The COM specifications needs an interface to be unique, e.g. it may not change, once it has been specified and deployed. To your application this means, even when you are plugging in newer DLL's, you still will be able to use an possibly older interface. It is the responsibility of the library writer to support older interfaces as well. At least your application will automatically recieve the information, that an interface is not avalaible any more.
During evolution of a software project it is natural that some things are changing. It is a always a challenge not to break compatibility unadvertently by this changes however. The RiSCANLIB handles compatibility in the following way:
The library comes with built in type information, so your compiler should be able to generate the necessary stub code to bind the library into your application. Depending on level of support from your compiler you then will be able to call into the library just as you would, using native objects. There are some differences however, so i.e. using Delphi you cannot use the assign operator "=" to read/write to member variables of the library object, but instead have to use get/set functions.

As already has been mentioned, there are two objects available. A configuration object named ScanCnf, and a measurement object named Scanner. Each of these objects corresponds to mutually exclusive states of the scanning device. At any time instance a scanner can be either in scanning or in configuration mode. After you have created the respective object, you will call Open on its interface. This call will only succeed when the scanner is not connected by any other means, so as e.g. a measurement object. The rule of thumb is to have only one object connected to a scanner at any time instance. An object is in connected state after Open succeded until Close has been successfully executed. You need not delete the object from memory, after a Close but can reuse it for following connections. In the case of the Scanner object even the state data (the measurement data) is valid after a Close. This is useful for overlapped processing, where you can read and process the measurement results, while starting to configure the scanner at the same time. Of course you can instantiate multiple objects to get simultaneous connections to more than one scanner on a single machine.

Most of the interface functions execute asynchronously, i.e. they initiate a operation on the scanner without waiting for the results. However this does not mean, that you do not need to care for the results. When the operation has been carried out, either successfully or with error, a windows message is sent back to the caller, informing the application about the outcome of the operation. The message is sent to a window, the handle of which is specified in the Open call. You may specify 0 for the window handle, in which the calls become synchronous, i.e. they wait for the completion of the operation and cannot be interrupted. In case of a failure, such as a non existent scanner on a port this might render your application frozen, and the only remedy would be to "kill" it. Obviously such programming practice is not recommended. So this use on a live connection to a scanner therefore is not recommended at all. You should use this mode mainly to read data from files.


VisualC++

These instructions do not attempt to tell you how to program to the COM interface, instead it will try to give you a fast path to use the library. There are a lot of different ways you can access COM objects. In this example we use the 'smart pointer' feature of the ATL.
First you must make sure the com library is initialized. You typically will need to call CoInitialize(NULL) from the InitInstance of your application. You should also add a call to CoUninitialize() in ExitInstance.
Next you will include an import statement to generate the class definitions from the type library.
E.g.:
#import "scannermod.dll" no_namespace
Then you will declare a variable of type IScannerPtr as
IScannerPtr pScanner(__uuidof(Scanner));
Now you are ready to access any of the documented functions of the class. The code snippet below shows you how to open the connection, acquire a frame and retrieve geometry data. Afterwards the connection will be closed.
pScanner->Open("\\.\\RiPT0","",(DWORD)hWnd, SCN_MFRAME, WM_USER);
  .
  .
  .
// somewhere in the message handler for WM_USER of the window hWnd:
switch(wParam) {
	case SCN_OPEN:
		pScanner->GetDimension(&nLines, &nMeas);
		pdVertex = (float*)calloc(3*nLines*nMeas, sizeof(float));
		for (n=0; n<3*nLines*nMeas; n++) *(pdVertex+n) = 0.0;
		pScanner->NextFrame();
		break;
	case SCN_FRAME:
		m_nFrame = lParam;
		pScanner->GetVertex(&nTransfered, pdVertex, SCN_CARTESIAN, 3, 0, nLines*m_nMeas);
		pScanner->Close();
		// now display the data
		break;
	case SCN_ERROR:
		m_pScanner->GetErrNmb(&nError);
		m_pScanner->GetErrStr(nError, &BSTRError);
		// e.g. display error
		break;
	case SCN_CLOSE:
		// do anything necessary after file has been closed
		// e.g update UI
		break;
}
  .
  .
  .
The project named simple on the distribution media shows a working example of how to use library from Visual C++. Using the library from a console application is demonstrated in the example project conv2asc.
The example project ctrl demonstrates how to use the ScanCnf library to control the scanner settings.

Delphi

For making the scanmod functions accessible to your DELPHI application, you need to
1. Register the library as described in Installation.
2. add a unit specifying the library functions, types and constants to your application project.

Delphi supports an automatic generation of an interface unit when importing a type library. This is done by the following steps:
After specifiying SCANNERMODlib_TLB in the uses clause of your source file like
    uses SCANNERMODlib_TLB
you then need to declare a scanner object of type IScanner
    var
        Scanner: IScanner;   // the scanner object for interfacing the RIEGL scanner   
and create the object with a line (e.g. in your main formular "FormCreate" event)
    Scanner := CoScanner.Create;
 
Additionally you need a message handler to handle the scanner messages
    procedure OnScanner(var aMessage:TMessage); message WM_SCANNER;
where WM_SCANNER is a constant which is usually set to a value within a user reserved area of windows messages starting at WM_USER.
    const
        WM_SCANNER = WM_USER + 0;       // user defined messages
To connect to the scanner (or to a data file) you call the open function in a way like
    Scanner.Open('\\.\ECP0','', Self.Handle, SCN_MLINE or SCN_MFRAME, WM_SCANNER);
To start acquistion of a frame call the NextFrame function
    Scanner.NextFrame;
and in the handler for the scanner messages you read the data in the SCN_LINE or SCN_FRAME event via the GetVertex function (e.g. for reading 2D coordinates)
procedure TMainForm.OnScanner(var aMessage:TMessage);
var
    DataPointsTransfered: SYSINT;      // to hold number of data points actually read
	
begin
    case aMessage.wParam of

        SCN_OPEN:
        begin
            // get the size of the frame
            // LinesPerImage and MeasPerLine are global SYSINTs
            Scanner.GetDimension(LinesPerImage,MeasPerLine);
        end;

        SCN_LINE:
        begin
            // get the scanner coordinates
            // it is assumed that a data array
            // Data: array[0..1][0..MeasPerLine-1] of single
            // for data storage exists
            Scanner.GetVertex(DataPointsTransfered,Data[0][0],SCN_CARTESIAN,2,MeasPerLine*aMessage.lParam,1);
        end;
    end;	
end;
The project named ScanLibExample on the distribution media shows a working example of how to use library from Delphi.

CBuilder

For making the scanmod functions accessible to your Borland C++Builder application, you need to
1. Register the library as described in Installation.
2. add a unit specifying the library functions, types and constants to your application project.

CBuilder supports an automatic generation of an interface unit when importing a type library. This is done by the following steps:
After specifiying SCANNERMODlib_TLB using the include directive in your source file like
    #include <SCANNERMODlib_TLB.h>
you then need to declare a scanner object of type IScannerPtr
    
    IScannerPtr pScanner;  // the scanner interface object
Call CoInitialize(NULL); if necessary
and create the object with a line (e.g. in your main formular "FormCreate" event)
   pScanner.CreateInstance(CLSID_Scanner); // create scanner object
Additionally you need a message handler to handle the scanner messages (e.g. a private method of a TForm object)
    void OnScanner(TMessage& aMessage); 
    BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(WM_SCANNER, TMessage, OnScanner)
    END_MESSAGE_MAP(TForm)
where WM_SCANNER is a constant which is usually set to a value within a user reserved area of windows messages starting at WM_USER.
    const
        WM_SCANNER = WM_USER + 0;       // user defined messages
To connect to the scanner (or to a data file) you call the open function in a way like
    pScanner->Open('\\.\ECP0', '', (unsigned long)this->Handle, SCN_MLINE | SCN_MFRAME, WM_SCANNER);
To start acquistion of a frame call the NextFrame function
    pScanner->NextFrame();
and in the handler for the scanner messages you read the data in the SCN_LINE or SCN_FRAME event via the GetVertex function (e.g. for reading 2D coordinates)
void TMainForm::OnScanner(TMessage& aMessage)
{
    int DataPointsTransfered;      // to hold number of data points actually read
	
    switch (aMessage.wParam)
    {
        case SCN_OPEN:

            // get the size of the frame
            // LinesPerImage and MeasPerLine are global SYSINTs

            pScanner->GetDimension(LinesPerImage, MeasPerLine);
            break;

        case SCN_LINE:

            // get the scanner coordinates
            // it is assumed that a data array
            // Data: array[0..1][0..MeasPerLine-1] of single
            // for data storage exists

            pScanner->GetVertex(DataPointsTransfered,Data[0][0],SCN_CARTESIAN,2,MeasPerLine*aMessage.lParam,1);
            break;
    }
}
When you do not need the scanner object anymore, release it with
pScanner.Release();    // release scanner object
The project named ScanLibConfigExample on the distribution media shows a working example of how to use library from CBuilder.