OPN Companion SDK for developing .NET application

This is the official .NET SDK from Opticon Sensors Europe BV for the OPN Companion Scanners to create C# applications to interface with OPN Companion devices using the Nuget Opticon.csp2 package

Features

  • Provides a C# interface for communicating with Opticon Companion devices using the CSP2 native functionality.
  • Supports .NET Standard 2.0/2.1, .NET 6, .NET 8, and .NET Framework 4.6.1+.
  • Compatible with WinForms, WPF, Console applications, and Windows Services.
  • Simplifies device connection, polling, barcode reading, and parameter management

Installation

Install via NuGet:

dotnet add package Opticon.csp2

Or search for 'Opticon.csp2' in the Visual Studio NuGet Package Manager.

Compatibility

Platform Supported
.NET Framework 4.6.1+
.NET Standard 2.0
.NET 6 / .NET 8

API

The documentation of the API can be found here: API

Usage

The following example demonstrates how to poll for devices, retrieve barcode data and manage device parameters.

using Opticon;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Console.WriteLine(String.Format("Csp2 DLL Version = {0}", OpnEnvironment.GetDllVersion()));

            var connectedDevices = new HashSet<int>();   // Used to avoid duplicate connection messages

            OpnDevice.StartPolling(device =>
            {
                try
                {
                    if (device.IsConnected)
                    {
                        string deviceId = device.GetDeviceId();
                        string model = device.GetModel();

                        // Handle new connection
                        if (connectedDevices.Add(device.Port))
                        {
                            Console.WriteLine($"[{model}] [{deviceId}] [COM{device.Port}] Connected ({device.GetSoftwareVersion()})");
                        }

                        // Handle barcode data
                        if (device.IsDataAvailable)
                        {
                            // Read all barcodes from the device and store them in a list
                            var barcodes = device.ReadBarcodes();

                            Console.WriteLine($"[{device.GetModel()}] [{deviceId}] [COM{device.Port}] {barcodes.Count} Barcode(s) Read");

                            foreach (var barcode in barcodes)
                            {
                                Console.WriteLine($"[{device.GetModel()}] [{deviceId}] [COM{device.Port}] [{barcode.Timestamp}] [{barcode.Data}] [{barcode.SymbologyName}]");
                            }

                            device.ClearBarcodes();
                        }

                        // Demonstrates the reading and writing of all parameter types (bool, int, enum and string/byte array)
                        device.GetParameter(OpnParameter.Code39, out bool enabled);

                        device.GetParameter(OpnParameter.ScannerOnTime, out int time);

                        device.GetParameter(OpnParameter.DeleteEnable, out DeleteEnableOptions deleteOptions);

                        device.SetParameter(OpnParameter.Code39, true);

                        device.SetParameter(OpnParameter.ScannerOnTime, 20);

                        device.SetParameter(OpnParameter.Gs1DataBar, Gs1DataBarOptions.Gs1DataBar | Gs1DataBarOptions.Gs1Expanded);

                        device.SetParameter(OpnParameter.ScratchPad, "Hello");

                        device.GetTime(out DateTime dTime);

                        device.SetTime(DateTime.Now);       // Sync device time with PC time
                    }
                    else
                    {
                        // Handle disconnect
                        if (connectedDevices.Remove(device.Port))
                        {
                            Console.WriteLine($"[{device.GetModel()}] [{device.GetDeviceId()}] [COM{device.Port}] Disconnected");
                        }
                    }
                    return 1; // Return 1 to indicate the device was successfully processed
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Exception occurred: {ex.Message}");
                    return 0; // Return 0 to continue polling, so we can retry later
                }
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Failed to start polling: {ex.Message}");
        }

        Console.ReadLine();
    }
}

The next example demonstrates how to use a multi-threaded approach to poll for devices and read barcodes without blocking the main thread.

using Opticon;
using System.Collections.Concurrent;

class Program
{
    enum WorkerState
    {
        Idle,
        Running,
        Success,
        Failed
    }

    static void Main(string[] args)
    {
        try
        {
            Console.WriteLine($"Csp2 DLL Version = {OpnEnvironment.GetDllVersion()}");

            var workerStates = new ConcurrentDictionary<int, WorkerState>();

            OpnDevice.StartPolling(device =>
            {
                try
                {
                    if (device.IsConnected)
                    {
                        // Start worker if none is running
                        if (workerStates.GetOrAdd(device.Port, WorkerState.Idle) == WorkerState.Idle || workerStates[device.Port] == WorkerState.Failed)
                        {
                            workerStates[device.Port] = WorkerState.Running;

                            Task.Run(() =>
                            {
                                try
                                {
                                    string deviceId = device.GetDeviceId();
                                    string model = device.GetModel();

                                    if (workerStates[device.Port] == WorkerState.Running) 
                                    {
                                        Console.WriteLine($"[{model}] [{deviceId}] [COM{device.Port}] Connected ({device.GetSoftwareVersion()})");
                                    }

                                    if (device.IsDataAvailable)
                                    {
                                        int cnt = 0;

                                        foreach (var barcode in device.EnumerateBarcodes())
                                        {
                                            Console.WriteLine($"[{++cnt}][{model}] [{deviceId}] [COM{device.Port}] [{barcode.Timestamp}] [{barcode.Data}] [{barcode.SymbologyName}]");
                                        }
                                    }

                                    device.SetTime(DateTime.Now);

                                    device.ClearBarcodes();

                                    workerStates[device.Port] = WorkerState.Success;
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine($"Worker exception (COM{device.Port}): {ex.Message}");
                                    workerStates[device.Port] = WorkerState.Failed;
                                }
                            });
                        }

                        // Decide return value based on worker state
                        if (workerStates.TryGetValue(device.Port, out var state))
                        {
                            return state == WorkerState.Success ? 1 : 0;
                        }

                        return 0;
                    }
                    else
                    {
                        if (workerStates.TryRemove(device.Port, out _))
                        {
                            Console.WriteLine($"[{device.GetModel()}] [{device.GetDeviceId()}] [COM{device.Port}] Disconnected");
                        }
                        return 0;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Exception occurred: {ex.Message}");
                    return 0;
                }
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Failed to start polling: {ex.Message}");
        }

        Console.ReadLine();
    }
}

Troubleshooting

Runtime Errors / DLL Not Found

This NuGet package of the CSP2 .NET wrapper should automatically add the correct native Csp2.dll to your output directory. If any DLL errors occur, verify that the correct native Csp2.dll is present in your application's output directory. Ensure the native binary matches your target architecture (x86/x64).

License

This project is licensed under the MIT License.