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.
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(); } }