Asynchronous Functions

From Phidgets Support
Revision as of 16:22, 18 June 2021 by Jdecoux (talk | contribs)

What are Asynchronous Functions?

Click to Enlarge

Asynchronous functions are a way to queue up calls to your VINT devices without waiting for return codes. This allows your program to carry on as quickly as possible after setting a property on a VINT device.

They are comprised of two parts: the function call, and the async handler. In order to use asynchronous function calls, you will call the function to do something with a Phidget channel, and assign said function an async handler. The function call will queue up the related communications and return immediately. The async handler will be called once the relevant communications have been completed to provide the return code from the Phidget.

Asynchronous calls issued at the same time to multiple VINT channels may be dispatched in quick succession before the Phidget22 back-end waits for return codes from any of the channels. This can significantly reduce latency when talking to multiple VINT channels.

Multiple asynchronous calls to the same channel in rapid succession will be queued up and issued one at a time as return codes are received for each.

Since these function calls are queued up, if you issue asynchronous calls faster than they can be dispatched and fully handled, they will begin to back up and potentially add latency to your program (even though your program will continue seamlessly queueing async commands). In extreme cases, if you queue up enough asynchronous calls, newer calls will be dropped, and your async handler will complete with Phidget Error 0x10 NoSpace. If you start seeing these errors in your async handler, you should re-evaluate your program and figure out how to make fewer calls to the asynchronous functions.

Ideas for Regulating Asynchronous Function Calls

To help you get started on deciding ways to prevent your application from overloading the asynchronous function call buffers, we have provided the following example, to both serve as a functional program, and as an outline for other languages.

This program uses a simple method of using the async handler to count the number of completed functions, and compares it to the number of asynchronous calls that were issued. The program will not call more asynchronous functions until all previous calls have completed, all while not blocking the rest of the loop in the process.

This example is intended to demonstrate how you could, for example, update the state of an output from within a very fast control loop, without slowing down either the update rate of the output, or the control loop itself.

See the API page for your device for a simpler example of how to use asynchronous functions from within your application, or for examples in languages not included here.

from Phidget22.Phidget import *
from Phidget22.Devices.DigitalOutput import *
from Phidget22.ErrorCode import *
import time

def main():
    digitalOutput0 = DigitalOutput()

    digitalOutput0.openWaitForAttachment(5000)

    #Assign new variables to track the number of calls made vs completed
    digitalOutput0.AsyncCalls = 0
    digitalOutput0.AsyncComplete = 0
    def AsyncResult(ch, res, details):
        ch.AsyncComplete += 1
        print("Async Complete:" + str(ch.AsyncComplete))
        if res != ErrorCode.EPHIDGET_OK:
            print("Async failure: %i : %s" % (res, details))
    
    while(1): #some loop condition
        #Here we check if the previous asynchronous call has completed before issuing a new one
        #If you had multiple asynchronous calls to make, you could (for example) give each its
        #own counter, or call a new batch of them once an entire set completes
        if(digitalOutput0.AsyncCalls == digitalOutput0.AsyncComplete):
            digitalOutput0.AsyncCalls += 1
            digitalOutput0.setDutyCycle_async(1, AsyncResult)
            # NOTE: Make sure to wait for async call to complete before closing the channel
        
        #Do other work here...
        

    digitalOutput0.close()

main()

import com.phidget22.*;
import java.io.IOException;

public class JavaApplication11 {

    static int asyncCalls = 0;
    static int asyncComplete = 0;
    
    public static void main(String[] args) throws Exception {
        
        Phidget.resetLibrary();
        DigitalOutput digitalOutput0 = new DigitalOutput();

        digitalOutput0.open(5000);
        
        while(asyncComplete < 1000) { //Some loop condition
            //Here we check if the previous asynchronous call has completed before issuing a new one
            //If you had multiple asynchronous calls to make, you could (for example) give each its
            //own counter, or call a new batch of them once an entire set completes
            if(asyncCalls == asyncComplete) {
                asyncCalls++;
                digitalOutput0.setDutyCycle(1, new AsyncListener() {
                    public void onAsyncCallback(AsyncResult ar) {
                        if (ar.getReturnCode() == ErrorCode.SUCCESS) {
                            asyncComplete++;
                            System.out.println("Async Complete: " + asyncComplete);
                        }
                        else
                            System.out.println("Async Failure: " + ar);
                    }
                });
                // NOTE: Make sure to wait for async call to complete before closing the channel
            }
            
            //Do other work here...
            Thread.sleep(1);
        }

        digitalOutput0.close();
    }
}

using System;
using Phidget22;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            DigitalOutput digitalOutput0 = new DigitalOutput();

            digitalOutput0.Open(5000);

            digitalOutput0.DutyCycle = 1;

            int asyncCalls = 0;
            int asyncComplete = 0;
            double dutyCycle = 0.1;
            while (true) /* Some loop condition */
            {
                //Here we check if the previous asynchronous call has completed before issuing a new one
                //If you had multiple asynchronous calls to make, you could (for example) give each its
                //own counter, or call a new batch of them once an entire set completes
                if (asyncCalls == asyncComplete)
                {
                    asyncCalls++;
                    digitalOutput0.BeginSetDutyCycle(dutyCycle, delegate (IAsyncResult result)
                    {
                        try
                        {
                            asyncComplete++;
                            digitalOutput0.EndSetDutyCycle(result);
                            Console.WriteLine("Async Complete: " + asyncComplete.ToString());
                        }
                        catch (PhidgetException ex)
                        {
                            Console.WriteLine("Async Failure: " + ex.Message);
                        }
                    }, null);
                }

                /*
                 *  Do other work were
                 */
            }
            digitalOutput0.Close();
        }
    }
}

#include <phidget22.h>
#include <stdio.h>

void CCONV setDutyCycleDone(PhidgetHandle phid, void *ctx, PhidgetReturnCode res) {
    int *asyncComplete = (int*)ctx;
    (*asyncComplete)++;
    printf("Async Complete : %d\n", *asyncComplete);
}

int main() {    
    PhidgetDigitalOutputHandle digitalOutput0;
    int asyncComplete;
    int asyncCalls;

    PhidgetDigitalOutput_create(&digitalOutput0);

    Phidget_openWaitForAttachment((PhidgetHandle)digitalOutput0, 5000);

    PhidgetDigitalOutput_setDutyCycle(digitalOutput0, 1);

    asyncComplete = 0;
    asyncCalls = 0;
    while (1) { //Some loop condition

        //Here we check if the previous asynchronous call has completed before issuing a new one
        //If you had multiple asynchronous calls to make, you could (for example) give each its
        //own counter, or call a new batch of them once an entire set completes
        if (asyncCalls == asyncComplete) {
            asyncCalls++;
            PhidgetDigitalOutput_setDutyCycle_async(digitalOutput0, 1, setDutyCycleDone, &asyncComplete);
        }

        //Do other work here...

    }
    Phidget_close((PhidgetHandle)digitalOutput0);

    PhidgetDigitalOutput_delete(&digitalOutput0);
}