private static void Main(string[] args) { // Display the API version to console output for visual confirmation as to // the version being used for this example. Console.WriteLine($"{nameof(JoeScan.Pinchot)}: {VersionInformation.Version}"); // Grab the serial number of the scan head from the command line. if (args.Length == 0) { Console.WriteLine("Must provide a scan head serial number as argument."); return; } foreach (var argument in args) { if (!uint.TryParse(argument, out var scanHeadSerialNumber)) { Console.WriteLine($"Argument {argument} cannot be parsed as a uint."); return; } ScanHeadSerialNumbers.Add(scanHeadSerialNumber); } // First step is to create a scan manager to manage the scan heads. _scanSystem = new ScanSystem(); // Create a scan head for each serial number passed in on the command line // and configure each one with the same parameters. Note that there is // nothing stopping users from configuring each scan head independently. var id = 0U; foreach (var serialNumber in ScanHeadSerialNumbers) { _scanSystem.CreateScanHead(serialNumber, id++); } var configuration = new ScanHeadConfiguration { ScanPhaseOffset = 0, LaserDetectionThreshold = 120, SaturationThreshold = 800, SaturatedPercentage = 30 }; configuration.SetCameraExposureTime(10000, 47000, 900000); configuration.SetLaserOnTime(100, 100, 1000); var scanWindow = ScanWindow.CreateScanWindowRectangular(20.0, -20.0, -20.0, 20.0); foreach (var scanHead in _scanSystem.ScanHeads) { scanHead.Configure(configuration); scanHead.SetWindow(scanWindow); scanHead.SetAlignment(0, 0, 0, ScanHeadOrientation.CableIsUpstream); } // Now that the scan heads are configured, we'll connect to the heads. var scanHeadsThatFailedToConnect = _scanSystem.Connect(TimeSpan.FromSeconds(3)); foreach (var scanHead in scanHeadsThatFailedToConnect) { Console.WriteLine($"Failed to connect to scan head {scanHead.SerialNumber}."); } if (scanHeadsThatFailedToConnect.Count > 0) { return; } // Once configured, we can then read the maximum scan rate of the scan // system. This rate is governed by the scan head with the lowest scan // rate based off window size and exposure values. double maxScanRate = _scanSystem.GetMaxScanRate(); Console.WriteLine($"The system has a maximum scan rate of {maxScanRate}Hz."); // To begin scanning on all of the scan heads, all we need to do is // command the scan system to start scanning. This will cause all of the // scan heads associated with it to begin scanning at the specified rate // and data format. const double rate = 900; const DataFormat format = DataFormat.XYFullLMFull; _scanSystem.StartScanning(rate, format); // In order to achieve a performant application, we'll create a thread // for each scan head. This allows the CPU load of reading out profiles // to be distributed across all the cores available on the system rather // than keeping the heavy lifting in an application within a single process. var threads = new List <Thread>(); foreach (var scanHead in _scanSystem.ScanHeads) { var thread = new Thread(() => Receiver(scanHead)); thread.Start(); threads.Add(thread); } // Put this thread to sleep until the total scan time is done. Thread.Sleep(1000); // Calling `ScanSystem.StopScanning()` will return immediately rather than // blocking until the scan heads have fully stopped. As a consequence, we // will need to add a small delay before the scan heads begin sending // new status updates. _scanSystem.StopScanning(); Thread.Sleep(2000); // We can verify that we received all of the profiles sent by the scan // heads by reading each scan head's status message and summing up the // number of profiles that were sent. If everything went well and the // CPU load didn't exceed what the system can manage, this value should // be equal to the number of profiles we received in this application. var expectedProfilesCount = _scanSystem.ScanHeads.Sum(scanHead => scanHead.Status.ProfilesSentCount); Console.WriteLine( $"Number of profiles received: {_profilesCount}, number of profiles expected: {expectedProfilesCount}"); // Wait for each thread to terminate before disposing resources. foreach (var thread in threads) { thread.Join(); } // Free resources. _scanSystem.Dispose(); }
private static void Main(string[] args) { // Display the API version to console output for visual confirmation as to // the version being used for this example. Console.WriteLine($"{nameof(JoeScan.Pinchot)}: {VersionInformation.Version}"); // Grab the serial number of the scan head from the command line. if (args.Length == 0) { Console.WriteLine("Must provide a scan head serial number as argument."); return; } if (!uint.TryParse(args[0], out _scanHeadSerialNumber)) { 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 manager // software object. This object will be used to manage groupings of scan // heads, telling them when to start and stop scanning. _scanSystem = new ScanSystem(); // Create a scan head software object for the user's specified serial // number and associate it with the scan manager 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. var scanHead = _scanSystem.CreateScanHead(_scanHeadSerialNumber, 1); // Now that we have successfully created the required software objects // needed to interface with the scan head and the scan system it is // associated with, we can begin to configure the scan head. // 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 { ScanPhaseOffset = 0, LaserDetectionThreshold = 120, SaturationThreshold = 800, SaturatedPercentage = 30 }; configuration.SetCameraExposureTime(10000, 47000, 900000); 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; filtering 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, ScanHeadOrientation.CableIsUpstream); // 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 scanHeadsThatFailedToConnect = _scanSystem.Connect(TimeSpan.FromSeconds(3)); if (scanHeadsThatFailedToConnect.Count > 0) { Console.WriteLine("Failed to connect to scan system."); return; } // Now that we are connected, we can query the scan head to get its // current status. Note that the status will be updated periodically by the // scan head and calling this function multiple times will provide the // last reported status of the scan head. PrintScanHeadStatus(scanHead.Status); // Once connected, this is the point where we could command the scan system // to start scanning; obtaining 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(); // Clean up resources allocated by the scan manager. _scanSystem.Dispose(); }
private static void Main(string[] args) { // Display the API version to console output for visual confirmation as to // the version being used for this example. Console.WriteLine($"{nameof(JoeScan.Pinchot)}: {VersionInformation.Version}"); // Grab the serial number of the scan head from the command line. if (args.Length == 0) { Console.WriteLine("Must provide a scan head serial number as argument."); return; } foreach (var argument in args) { if (!uint.TryParse(argument, out var scanHeadSerialNumber)) { Console.WriteLine($"Argument {argument} cannot be parsed as a uint."); return; } ScanHeadSerialNumbers.Add(scanHeadSerialNumber); } // First step is to create a scan manager to manage the scan heads. _scanSystem = new ScanSystem(); // Create a scan head software object for each serial number passed in // through the command line. We'll assign each one a unique ID starting at // zero; we'll use this as an easy index for associating profile data with // a given scan head. var id = 0U; foreach (var serialNumber in ScanHeadSerialNumbers) { _scanSystem.CreateScanHead(serialNumber, id++); } // For this example application, we'll just use the same configuration // settings we made use of in the "Configure and Connect" example. The // only real difference here is that we will be applying this configuration // to multiple scan heads, using a "for" loop to configure each scan head // one after the other. var configuration = new ScanHeadConfiguration { ScanPhaseOffset = 0, LaserDetectionThreshold = 120, SaturationThreshold = 800, SaturatedPercentage = 30 }; configuration.SetCameraExposureTime(10000, 47000, 900000); configuration.SetLaserOnTime(100, 100, 1000); foreach (var scanHead in _scanSystem.ScanHeads) { scanHead.Configure(configuration); // To illustrate that each scan head can be configured independently, // we'll alternate between two different windows for each scan head. The // other options we will leave the same only for the sake of convenience; // these can be independently configured as needed. var scanWindow = scanHead.ID % 2 == 0 ? ScanWindow.CreateScanWindowRectangular(20.0, -20.0, -20.0, 20.0) : ScanWindow.CreateScanWindowRectangular(30.0, -30.0, -30.0, 30.0); scanHead.SetWindow(scanWindow); scanHead.SetAlignment(0, 0, 0, ScanHeadOrientation.CableIsUpstream); } // Now that the scan heads are configured, we'll connect to the heads. var scanHeadsThatFailedToConnect = _scanSystem.Connect(TimeSpan.FromSeconds(3)); foreach (var scanHead in scanHeadsThatFailedToConnect) { Console.WriteLine($"Failed to connect to scan head {scanHead.SerialNumber}."); } if (scanHeadsThatFailedToConnect.Count > 0) { return; } // Once configured, we can then read the maximum scan rate of the scan // system. This rate is governed by the scan head with the lowest scan // rate based off window size and exposure values. double maxScanRate = _scanSystem.GetMaxScanRate(); Console.WriteLine($"The system has a maximum scan rate of {maxScanRate}Hz."); // To begin scanning on all of the scan heads, all we need to do is // command the scan system to start scanning. This will cause all of the // scan heads associated with it to begin scanning at the specified rate // and data format. const double rate = 400; const DataFormat format = DataFormat.XYFullLMFull; _scanSystem.StartScanning(rate, format); // We'll read out a small number of profiles for each scan head, servicing // each one in a round robin fashion until the requested number of profiles // have been obtained. const int maxProfiles = 10; var timeout = TimeSpan.FromSeconds(10); var stopwatch = Stopwatch.StartNew(); var profiles = _scanSystem.ScanHeads.ToDictionary(scanHead => scanHead, scanHead => new List <Profile>()); while (profiles.Values.Any(l => l.Count < maxProfiles)) { foreach (var scanHead in _scanSystem.ScanHeads) { scanHead.TryTakeNextProfile(out var profile, TimeSpan.FromMilliseconds(100), new CancellationToken()); if (profile is null) { continue; } profiles[scanHead].Add(profile); } if (stopwatch.Elapsed > timeout) { Console.WriteLine($"Timed-out waiting to collect {maxProfiles} profiles."); return; } } // We've collected all of our data; time to stop scanning. Calling this // function will cause each scan head within the entire scan system to // stop scanning. Once we're done scanning, we'll process the data. _scanSystem.StopScanning(); foreach (var scanHead in _scanSystem.ScanHeads) { var highestPoint = FindScanProfileHighestPoint(profiles[scanHead]); Console.WriteLine($"Highest point from scan head {scanHead.ID} is X: {highestPoint.X:F3}\tY: {highestPoint.Y:F3}\tBrightness: {highestPoint.Brightness}"); } // Free resources. _scanSystem.Dispose(); }