Click or drag to resize

12.1 ClientLib and NativeLib API Implementations

The SP-ICE-3 Client API is provided as two implementations for each of the supported Host-PC platforms:

Windows
SP-ICE-3 API

DLL Name

intended Client Program Type

ClientLib

RAYLASE.SPICE3.ClientLib.dll

.Net Application , written using any of the .NET languages ( C#, VB.NET, etc)

NativeLib

RAYLASE.SPICE3.Native.ClientLib.dll

Native Application, written using any language which allows linking with DLLs via the stdc calling convention (e.g. C, C++ (unmanaged), python, FORTRAN IV, etc.)[Note 1]

GNU/Linux
SP-ICE-3 API

DLL or SO
Name

intended Client Program Type

ClientLib

RAYLASE.SPICE3.ClientLib.dll

.Net Application , written using any of the managed .NET languages ( C#, VB.NET, etc)

NativeLib

libRAYLASE.SPICE3.Native.ClientLib-1.##.#.so

Native Application, written using any language which allows linking with SOs via the stdc calling convention (e.g. C, C++ (unmanaged), python, FORTRAN IV, etc.)[Note 1]

The differences between the implementations are discussed below.

The ClientLib DLL

The main purpose of the SP-ICE-3 ClientLib is to abstract communications with the SP-ICE-3 Card, and to provide the programmer with comfortable access to the card's functionality.

The top-level ClientAPI class must be instantiated once for each card that your application program wishes to control. Thereafter, it serves two main purposes:

  • handles communications with the individual card;

  • provides access to the card's functional sub-units via members which each present an instance of a particular sub-unit's API.

Important note  Important
  • On Windows platforms, the SP-ICE-3 ClientLib requires .NET Framework v4.6.1 (or later).

  • On GNU/Linux platforms, the SP-ICE-3 ClientLib requires .NET 5.0 (or later).

Please make sure that a suitable version of the .NET Framework is installed on your Host-PC.

The NativeLib DLL

The NativeLib mirrors the ClientLib's functionality almost 1:1, so that the SP-ICE-3 Card can be easily accessed from unmanaged (native) applications.

Obviously, a few minor exceptions and allowances must be made:

  • All native functions appear in the global namespace.

  • For most of the methods defined by the sub-unit APIs in the ClientLib namespace, there are equivalent functions in the NativeLib, but they are renamed according to the convention:

    ClientLib API method

    NativeLib API equivalent function

    ClientAPI.<SubAPIMemberName>.<Methodname>(...)

    rl<SubAPIMemberName><Methodname>(handle, ...)

    Thus, for instance:

     

    client.Scanner.GetConfig()

    rlScannerGetConfig(hClient, *config)

    For futher examples, see below.

  • Method signatures remain generally unchanged in the NativeLib, EXCEPT for an additional leading parameter of type rlHandle, which is a handle to something appropriate:

    • in many cases, this will be the handle for a particular client-instance (i.e. for a particular SP-ICE-3 Card), as returned by rlConnect(...)[Note 2]

    • sometimes it might be a local structure instance such as a command list, etc.

  • Some methods in the ClientLib (managed) return a value but take no parameters.

  • Note  Note

    Native programs cannot use .NET-Exceptions, so for the NativeLib:

    • Most methods return an rlResult, indicating the success or failure of the call.

    • If the result is rlERROR, a call to rlGetLastError( char* buffer, int32_t bufferSize ) can be used to determine what went wrong.

      The string returned in buffer by rlGetLastError includes the name of the original exception type or class (e.g. RAYLASE.SPICE3VersionMismatchException), as well as a descriptive message.

  • The NativeLib does not provide equivalents for certain ClientLib functions which are implemented using Reflection.

Please note that the NativeLib implementation of the SP-ICE-3 Client API is NOT inherently thread-safe.

ClientLib vs NativeLib side-by-side code comparison.
Note  Note

This code is provided for comparison only, and is neither complete, nor guaranteed to run as it stands.

For the sake of simplicity, we assume that we already know the address of the SP-ICE-3 Card : otherwise, we would need to use the discovery mechanism.

Note in particular how much easier it is to implement proper error handling with .NET Exceptions ... :-)

Marking a square

ClientLib

NativeLib

C#
 1using System;
 2using RAYLASE.SPICE3.ClientLib;
 3
 4public class ClientLibExampleMarkSquare
 5{
 6    const string MyKnownCardAddress = "192.168.1.1";
 7
 8    public static void Main( string[] args )
 9    {
10        try
11        {
12            // establish a connection with the card
13            using ( ClientAPI client = new ClientAPI( MyKnownCardAddress ) )
14            {
15                // Reset the card to its default settings:
16                // This restores the following settings to the values that were last saved on the card:
17                // - scanner configuration (such as field size, field correction, etc.)
18                // - laser configuration (such as gate/LM timing parameters, power target, etc.)
19                // - process variables (such as jump speed, settling times, mark speed, etc.)
20                client.System.ResetToDefaults();
21
22                // do the real work here
23                MarkSquare( client );
24            }
25        }
26        catch ( Exception ex )
27        {
28            Console.WriteLine( ex.ToString() );
29        }
30    }
31
32    public static void MarkSquare( ClientAPI client )
33    {
34        // get the scanner's current field size
35        double fieldSize = client.Scanner.GetConfig().FieldSizeX;
36
37        double squareSize = 32760 / 32768.0 * fieldSize;
38
39        double jumpSpeed = 10.0;
40        double markSpeed = 3.0;
41
42        // Create a list locally on the host computer,
43        // and fill it with macro-vectors defining a square.
44        int listID = 0;
45        Console.WriteLine( "Preparing list: ID={1}...", listID );
46        CommandList list = new CommandList();
47
48        list.AppendJumpSpeed( jumpSpeed );
49
50        list.AppendMarkSpeed( markSpeed );
51
52        list.AppendJumpAbs( 0, 0 );
53
54        list.AppendJumpRel( -squareSize / 2, -squareSize / 2 );
55
56        list.AppendMarkRel( squareSize, 0 );
57
58        list.AppendMarkRel( 0, squareSize );
59
60        list.AppendMarkRel( -squareSize, 0 );
61
62        list.AppendMarkRel( 0, -squareSize );
63
64        // transfer list contents to card
65        client.List.Set( listID, list );
66
67        // run the list, and wait until its execution has completed
68        Console.WriteLine( "Executing list: ID={1}...", listID );
69        client.List.Execute( listID );
70
71        Nullable<int> timeoutMs = 30000;
72        Nullable<int> doneID;
73
74        Console.WriteLine( "Waiting until list execution is done: ID={1}...", listID );
75        if ( !client.List.WaitForListDone( out doneID, timeoutMs ) )
76            throw new Exception( "Timed out waiting for ListDone" );
77
78        Console.WriteLine( "List done: ID={1}...", doneID );
79
80        // delete list on the card: this frees the memory occupied by the list on the card.
81        Console.WriteLine( "Deleting list: ID={1}...", listID );
82        client.List.Delete( listID );
83
84        // 
85        // (No need to "delete" the local list.)
86        // 
87    }
88}
C++
  1#include <stdio.h>
  2#include "ClientAPI.h"
  3
  4#define MY_KNOWN_CARD_ADDRESS "192.168.1.1"
  5static rlResult markSquare(rlHandle handle);
  6static int printLastError(void);
  7
  8int _tmain(int argc, char* argv[])
  9{
 10    // establish a connection with the card
 11    rlHandle handle = rlConnect(MY_KNOWN_CARD_ADDRESS, 49374);
 12    if (handle < 0)
 13        return printLastError();
 14
 15    rlResult status = rlSUCCESS;
 16
 17    // Reset the card to its default settings:
 18    // This restores the following settings to the values that were last saved on the card:
 19    // - scanner configuration (such as field size, field correction, etc.)
 20    // - laser configuration (such as gate/LM timing parameters, power target, etc.)
 21    // - process variables (such as jump speed, settling times, mark speed, etc.)
 22    if ((status = rlSystemResetToDefaults(handle)) != rlSUCCESS)
 23        goto Cleanup;
 24
 25    // do the real work here
 26    status = MarkSquare(handle));
 27
 28Cleanup:
 29    // print any errors
 30    if (status != rlSUCCESS)
 31    printLastError();
 32
 33    // disconnect from the card
 34    printf("Disconnecting from card...\n");
 35    if (rlDisconnect(handle) != rlSUCCESS)
 36    return printLastError();
 37
 38    printf("Exiting with status = %d\n", status);
 39    return status;
 40}
 41
 42static rlResult MarkSquare(rlHandle handle)
 43{
 44    rlResult result;
 45
 46    // get the scanner's current field size
 47    rlScannerConfig sc;
 48    if ((result = rlScannerGetConfig(handle, &sc)) != rlSUCCESS)
 49        return result;
 50
 51    double squareSize = 32760 / 32768.0 * fieldSize;
 52
 53    double jumpSpeed = 10.0;
 54    double markSpeed = 3.0;
 55
 56    // Create a list locally on the host computer,
 57    // and fill it with macro-vectors defining a square.
 58    int listIndex = 0;
 59    printf("Preparing list: index=%d...\n", listIndex);
 60    rlCommandListHandle list = rlListAllocate(handle, listIndex);
 61
 62    if ((result = rlListAppendJumpSpeed(list, jumpSpeed)) != rlSUCCESS)
 63        return result;
 64    if ((result = rlListAppendMarkSpeed(list, markSpeed)) != rlSUCCESS)
 65        return result;
 66    if ((result = rlListAppendJumpAbs2D(list, 0, 0)) != rlSUCCESS)
 67        return result;
 68    if ((result = rlListAppendJumpRel2D(list, -squareSize/2, -squareSize/2)) != rlSUCCESS)
 69        return result;
 70    if ((result = rlListAppendMarkRel2D(list, squareSize, 0)) != rlSUCCESS)
 71        return result;
 72    if ((result = rlListAppendMarkRel2D(list, 0, squareSize)) != rlSUCCESS)
 73        return result;
 74    if ((result = rlListAppendMarkRel2D(list, -squareSize, 0)) != rlSUCCESS)
 75        return result;
 76    if ((result = rlListAppendMarkRel2D(list, 0, -squareSize)) != rlSUCCESS)
 77        return result;
 78
 79    // close the list causing its contents to be transferred to the card
 80    printf("Closing list: index=%d...\n", listIndex);
 81    if ((result = rlListSet(handle, list)) != rlSUCCESS)
 82        return result;
 83
 84    // run the list, and wait until its execution has completed
 85    printf("Executing list: index=%d...\n", listIndex);
 86    if ((result = rlListExecute(handle, listIndex)) != rlSUCCESS)
 87        return result;
 88
 89    int timeoutMs = 30000;
 90    bool done = false;
 91    int32_t listID;
 92    printf("Waiting until list execution is done: index=%d...\n", listIndex);
 93    if ((result = rlListWaitForExecutionDone(handle, timeoutMs, &done, &listID)) != rlSUCCESS)
 94        return result;
 95    if (!done)
 96    {
 97        printf("Timeout: execution has not completed after %d ms!", timeoutMs);
 98        return rlERROR;
 99    }
100    printf("Execution done: index=%d...\n", listIndex);
101
102    // delete list on the card: this frees the card's memory occupied by the list
103    printf("Deleting list: index=%d...\n", listIndex);
104    if ((result = rlListDelete(handle, listIndex)) != rlSUCCESS)
105        return result;
106
107    // delete the local list: this frees the PC's memory occupied by the card
108    if ((result = rlListReleaseHandle(list)) != rlSUCCESS)
109        return result;
110
111    return result;
112}
113
114// helper function which displays the description of the last error
115static int printLastError(void)
116{
117    char errBuffer[1024] = { 0 };
118    rlGetLastError(errBuffer, sizeof(errBuffer) - 1);
119    printf("%s", errBuffer);
120    return rlERROR;
121}
Notes
  1. Ok, that last one is actually intended to be a joke...

  2. rlConnect(...) itself being one of the handful of NativeLib functions which have no direct equivalent in the ClientLib.