Skip to content

Configure And Connect

Updated Examples

For the most up-to-date examples, please see the public repository on GitHub at https://github.com/JoeScan-Inc/pinchot-net-api/tree/master/examples or the "examples" folder in the software distribution.

// Copyright(c) JoeScan Inc. All Rights Reserved.
//
// Licensed under the BSD 3 Clause License. See LICENSE.txt in the project
// root for license information.

// This example application provides a basic example of setting up a scan for
// scanning using functions and data structures from the Pinchot API. In the
// following order, the application will:
//
//    1. Create scan system and scan head
//    2. Print out the scan head's capabilities
//    3. Set the configuration for the scan head
//    4. Build a basic phase table
//    5. Connect to the scan head
//    6. Print out the scan head's current status
//    7. Disconnect from the scan head.
//


using JoeScan.Pinchot;

Console.WriteLine($"Pinchot API version: {VersionInformation.Version}");

// Grab the serial number of the scan head from the command line.
if (args.Length != 1)
{
    Console.WriteLine("Must provide a scan head serial number as argument.");
    return;
}

if (!uint.TryParse(args[0], out uint serialNumber))
{
    Console.WriteLine($"Argument {args[0]} cannot be parsed as a uint.");
    return;
}

// One of the first calls to the API should be to create a scan system.
// This object will be used to manage groupings of scan heads and to
// perform system-level functions such as starting and stopping scanning.
using var scanSystem = new ScanSystem(ScanSystemUnits.Inches);

// Create a scan head for the user's specified serial number and associate
// it with the scan system we just created. We'll also assign it a user
// defined ID that can be used within the application as an optional
// identifier if preferred over the serial number. Note that at this point
// we haven't connected with the physical scan head yet, but it must be
// on the network for this command to complete successfully.
var scanHead = scanSystem.CreateScanHead(serialNumber, id: 1);

// After a scan head has been created, we can query its firmware version
Console.WriteLine($"Scan head version: {scanHead.Version}");

// Many of the settings directly related to the operation of the cameras
// and lasers can be found in the `ScanHeadConfiguration` class. Refer
// to the API documentation for specific details regarding each field. For
// this example, we'll use some generic values not specifically set for any
// particular scenario.
var configuration = new ScanHeadConfiguration();
configuration.SetLaserOnTime(100, 100, 1000);
scanHead.Configure(configuration);

// Proper window selection can be crucial to successful scanning as it
// allows users to limit the region of interest for scanning which can
// filter out other sources of light that could complicate scanning.
// It is worth noting that there is an inverse relationship with the
// scan window and the overall scan rate a system can run at. Using
// larger scan windows will reduce the maximum scan rate of a system,
// whereas using a smaller scan window will increase the maximum scan rate.
var scanWindow = ScanWindow.CreateScanWindowRectangular(30.0, -30.0, -30.0, 30.0);
scanHead.SetWindow(scanWindow);

// Setting the alignment through the following function can help to correct
// for any mounting issues with a scan head that could affect the 3D measurement.
// For this example, we'll assume that the scan head is mounted perfectly such
// that the laser is pointed directly at the scan target.
scanHead.SetAlignment(0, 0, 0);

// The orientation of the scan head flips the data around the X axis in the
// event that a given JS-50 scanning device is oriented 180 degrees differently
// than other JS-50s within a system. This commonly happens if the orientation
// changes to facilitate better cable routing within a mill environment.
scanHead.Orientation = ScanHeadOrientation.CableIsUpstream;

// For this example we will create a basic phase table that utilizes all of
// the phasable elements of the scan head. Depending on the type of scan
// head, we will need to either schedule its cameras or its lasers. The
// bellow `switch` statement shows this process for each type of scan head.
switch (scanHead.Type)
{
    // camera-driven scan heads
    case ProductType.JS50WX or ProductType.JS50WSC or ProductType.JS50MX:
        // example phase table for an WX:
        //   Phase | Laser | Camera
        //     1   |   1   |   A
        //     2   |   1   |   B
        foreach (var camera in scanHead.Cameras)
        {
            scanSystem.AddPhase();
            scanSystem.AddPhaseElement(scanHead.ID, camera);
        }
        break;

    // laser-driven scan heads
    // for laser-driven scan heads, make sure to not schedule lasers
    // that coorespond to the same camera in back-to-back phases as
    // to not incur a throughput penalty
    case ProductType.JS50X6B20 or ProductType.JS50X6B30 or ProductType.JS50Z820 or ProductType.JS50Z830:
        // example phase table for an X6B:
        //   Phase | Laser | Camera
        //     1   |   1   |   B
        //     2   |   4   |   A
        //     3   |   2   |   B
        //     4   |   5   |   A
        //     5   |   3   |   B
        //     6   |   6   |   A
        int numLasers = scanHead.Lasers.Count();
        for (int i = 0; i < numLasers / 2; i++)
        {
            var laser = Laser.Laser1 + i;
            scanSystem.AddPhase();
            scanSystem.AddPhaseElement(scanHead.ID, laser);
            scanSystem.AddPhase();
            scanSystem.AddPhaseElement(scanHead.ID, laser + (numLasers / 2));
        }
    break;

    default:
        throw new InvalidOperationException($"Invalid scan head type {scanHead.Type}");
}

// We've now successfully configured the scan head. Now comes the time to
// connect to the physical scanner and transmit the configuration values
// we previously set up.
var connectTimeout = TimeSpan.FromSeconds(3);
var scanHeadsThatFailedToConnect = scanSystem.Connect(connectTimeout);
if (scanHeadsThatFailedToConnect.Count > 0)
{
    Console.WriteLine("Failed to connect to scan system.");
    return;
}

Console.WriteLine($"Connected to {scanHead.SerialNumber}");

// Now that we are connected, we can query the scan head to get its current status.
var status = scanHead.RequestStatus();
Console.WriteLine("Status");
Console.WriteLine($"\tGlobal time: {status.GlobalTimeNs} ns");
Console.WriteLine($"\tEncoder: {status.EncoderValues[Encoder.Main]}");

// The minimum scan period indicates the fastest that the scan system can
// obtain profiles. This value is dependent upon the laser on time, the
// size of the scan window, and the phase table. For this example with only
// one scan head, only its configuration affects the minimum scan period.
// With more scan heads in the scan system, they will collectively affect
// this value.
var minPeriodUs = scanSystem.GetMinScanPeriod();
Console.WriteLine($"Min scan period: {minPeriodUs} us");

// Once connected, this is the point where we could command the scan system
// to start scanning to obtain profile data from the scan heads associated
// with it. This will be the focus of a later example.

// We've accomplished what we set out to do for this example; now it's time
// to bring down our system.
scanSystem.Disconnect();