Click or drag to resize

12.6.1 Using a ListDone Callback

Event Driven programming is directly supported by methods available in the SP-ICE-3 ClientLib API.

Here is an example that uses ListAPIWaitForListDone.

Most of the Sample Applications provided with the SP-ICE-3 Software Installation Package, and many of the code examples shown in this Manual, demonstrate use of the Event Driven programming model with the ClientAPI.

Example of using a ListDone Callback
ListDone Callback example program.
public class ListDoneCallbackExample
{
    // insert the IP address or Hostname of your SP-ICE-3 card here.
    private readonly string _cardIP = "169.254.x.y";

    // Use IDs 0..9 for marking squares.
    private const int _maxSquareListID = 9;

    // Just some random ID which is not in the above range.
    private const int _gotoCenterOfFieldListID = 99;

    // An initial "invalid" value, also not equal to any of the other IDs used in this example.
    private int _lastDoneListID = -1;

    // The main thread waits on this event, which is signaled by the ListDoneCallback.
    private readonly AutoResetEvent _listDoneEvent = new AutoResetEvent( false );

    private void ListDoneCallback( ClientAPI client, int listID )
    {
        // Record the listID locally in a thread-safe manner.
        Interlocked.Exchange( ref _lastDoneListID, listID );

        // Tell the main thread that we've been called!
        _listDoneEvent.Set();
    }

    public void Run()
    {
        using ( ClientAPI client = new ClientAPI( _cardIP ) )
        {
            try
            {
                client.System.ResetToDefaults();

                client.List.RegisterListDoneCallback( ListDoneCallback );

                // For the purposes of this sample, make sure the scanners are at the origin so that
                // the expected execution time can be calculated correctly for ALL of the squares, including the very first.
                GotoCenterOfField( client, _gotoCenterOfFieldListID );

                for ( int listID = 0; listID <= _maxSquareListID; listID++ )
                {
                    // Construct each list with a different size so that they will have differing execution times.
                    CommandList list = BuildSquare( out int expectedListExecutionTimeMs, 10000 + ( listID * 1000 ) );

                    // Send the list to the card, and execute it.
                    MarkSquare( client, list, listID );

                    // Now wait for the card to tell us that it has finished executing the list.
                    // By the time we get here, the list has probably ALREADY started to execute, so we will
                    // not usually have to wait for the whole expectedListExecutionTime before the listDone event arrives.
                    // However, we also need to take the unpredictability of the network connection into account.
                    // Therefore we add a couple of seconds to the expectedListExecutionTime value, before using it as a timeout.
                    int waitForListDoneEventTimeoutMs = expectedListExecutionTimeMs + 2000;

                    Console.WriteLine( $"Waiting up to {waitForListDoneEventTimeoutMs} ms for list[{listID}] done." );

                    if ( !_listDoneEvent.WaitOne( waitForListDoneEventTimeoutMs ) )
                    {
                        throw new Exception( $"listDone[{listID}] not received within {waitForListDoneEventTimeoutMs} ms." );
                    }

                    // Retrieve the lastDoneListID value in a thread-safe manner, and
                    // make sure it's the one we were expecting.
                    if ( listID != Interlocked.CompareExchange( ref _lastDoneListID, listID, listID ) )
                    {
                        throw new Exception( $"listDone[{listID}] not received: got listID {_lastDoneListID} instead." );
                    }

                    // We did not wait in vain...
                    Console.WriteLine( $"List[{listID}] done. Execution stats: {client.List.GetLastExecutionStats()}." );
                    Console.WriteLine();
                }
            }
            finally
            {
                client.List.UnregisterListDoneCallback( ListDoneCallback );
            }
        }
    }

    private void MarkSquare( ClientAPI client, CommandList list, int listID )
    {
        Console.WriteLine( $"MarkSquare: listID {listID}" );

        client.List.Set( listID, list ); // Transfer the list to the card.

        client.List.Execute( listID ); // Tell the card to begin executing the list.
    }

    private CommandList BuildSquare( out int expectedExecutionTimeMs, double size )
    {
        double jumpSpeed = 5;
        double markSpeed = 0.2;

        CommandList list = new CommandList();
        list.AppendJumpSpeed( jumpSpeed );
        list.AppendJumpDelay( 0 );
        list.AppendMarkSpeed( markSpeed );
        list.AppendMarkDelay( 0 );
        list.AppendPolyDelay( 0 );
        list.AppendLmFrequency( 1.0 / 50 );
        list.AppendLmWidth( 35 );

        double halfsize = size / 2.0;

        list.AppendJumpAbs( -halfsize, -halfsize );
        list.AppendMarkAbs( halfsize, -halfsize );
        list.AppendMarkAbs( halfsize, halfsize );
        list.AppendMarkAbs( -halfsize, halfsize );
        list.AppendMarkAbs( -halfsize, -halfsize );
        list.AppendJumpAbs( Point2D.Zero );

        // Work out approximately how long we expect execution of this list to take.
        double expectedExecutionTimeUs = 2.0 * Math.Sqrt( 2 ) * halfsize / jumpSpeed;
        expectedExecutionTimeUs += 4.0 * size / markSpeed;
        expectedExecutionTimeMs = (int)( 1e-3 * expectedExecutionTimeUs );

        return list;
    }

    private void GotoCenterOfField( ClientAPI client, int listID )
    {
        CommandList list = new CommandList();
        list.AppendJumpSpeed( 5 );
        list.AppendJumpDelay( 0 );
        list.AppendJumpAbs( Point2D.Zero );

        Console.WriteLine( $"GotoCenterOfField: {listID}" );

        client.List.Set( listID, list );
        client.List.Execute( listID );

        // Since we don't know where the scanners are coming from,
        // we simply assume that they will not take more than 10s to reach the field origin.
        if ( !_listDoneEvent.WaitOne( TimeSpan.FromSeconds( 10 ) ) )
            throw new Exception( $"listDone[{listID}] not received within 10s." );
        Console.WriteLine( $"List[{listID}] done. Execution stats: {client.List.GetLastExecutionStats()}." );
        Console.WriteLine();
    }

    private static void Main( string[] args )
    {
        ListDoneCallbackExample p = new ListDoneCallbackExample();
        p.Run();
    }
}