Asynchronous Methods: Difference between revisions

From Phidgets Support
 
(2 intermediate revisions by 2 users not shown)
Line 19: Line 19:


===Multiple Channels Example===
===Multiple Channels Example===
[[Image:LED1000_0.jpg|thumb|link=https://wwwdev.int.phidgets.com/docs/images/5/58/LED1000_0.jpg|<center>32x Isolated LED Phidget (LED1000_0)</center>]]
[[Image:1032_0B.jpg|thumb|link=https://cdn.phidgets.com/docs/images/6/6d/1032_0B.jpg|<center>PhidgetLED-64 Advanced(1032_0)</center>]]
The [https://www.phidgets.com/?prodid=964 32x Isolated LED Phidget] features 32 channels, one for each LED. To turn all the LEDs on or off simultaneously, 32 sequential commands are necessary (one for each channel). In this situation, using asynchronous methods can drastically improve the performance of your application.
The [https://www.phidgets.com/?prodid=1024 PhidgetLED-64 Advanced] features 64 channels, one for each LED. To turn all the LEDs on or off simultaneously, 64 sequential commands are necessary (one for each channel). In this situation, using asynchronous methods can drastically improve the performance of your application.


===Multiple Devices Example===
===Multiple Devices Example===
Line 33: Line 33:


===Code Example===
===Code Example===
The code below provides a simple demonstration of how asynchronous methods can be used (while avoiding [[#Overloading|overloading]]).
This code sample shows how you can simultaneously change the state of two outputs on a device like the OUT1100. Without async, there would be a noticeable delay between the first and second output being set.


<tabber>
<tabber>
Line 39: Line 39:
<syntaxhighlight lang=python>
<syntaxhighlight lang=python>
from Phidget22.Phidget import *
from Phidget22.Phidget import *
from Phidget22.ErrorCode import *
from Phidget22.Devices.DigitalOutput import *
from Phidget22.Devices.DigitalOutput import *
from Phidget22.ErrorCode import *
import time
import time


def main():
def main():
    digitalOutput0 = DigitalOutput()
def AsyncResult(ch, res, details):
if res != ErrorCode.EPHIDGET_OK:
print("Async failure: %i : %s" % (res, details))
digitalOutput0 = DigitalOutput()
digitalOutput1 = DigitalOutput()
 
digitalOutput0.setChannel(0)
digitalOutput1.setChannel(1)


    digitalOutput0.openWaitForAttachment(5000)
digitalOutput0.openWaitForAttachment(5000)
digitalOutput1.openWaitForAttachment(5000)


    #Assign new variables to track the number of calls made vs completed
digitalOutput0.setState_async(True, AsyncResult)
    digitalOutput0.AsyncCalls = 0
digitalOutput1.setState_async(True, AsyncResult)
    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()
try:
input("Press Enter to Stop\n")
except (Exception, KeyboardInterrupt):
pass
 
digitalOutput0.close()
digitalOutput1.close()


main()
main()
Line 76: Line 75:
<syntaxhighlight lang=java>
<syntaxhighlight lang=java>
import com.phidget22.*;
import com.phidget22.*;
import java.util.Scanner;
import java.io.IOException;
import java.io.IOException;


public class JavaApplication11 {
class Java_Example {
 
    public static AsyncListener digitalOutputAsyncListener() {
        return new AsyncListener() {
            public void onAsyncCallback(AsyncResult ar) {
                if (ar.getReturnCode() != ErrorCode.SUCCESS)
                    System.out.println("Async Failure: " + ar);
            }
        };
    }
 
public static void main(String[] args) throws Exception {
DigitalOutput digitalOutput0 = new DigitalOutput();
DigitalOutput digitalOutput1 = new DigitalOutput();
 
digitalOutput0.setChannel(0);
digitalOutput1.setChannel(1);
 
digitalOutput0.open(5000);
digitalOutput1.open(5000);


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


        digitalOutput0.open(5000);
//Wait until Enter has been pressed before exiting
       
System.in.read();
        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();
digitalOutput0.close();
    }
digitalOutput1.close();
}
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 125: Line 118:
namespace ConsoleApplication
namespace ConsoleApplication
{
{
    class Program
class Program
    {
{
        static void Main(string[] args)
        {
            DigitalOutput digitalOutput0 = new DigitalOutput();


            digitalOutput0.Open(5000);
static async void Main(string[] args)
{
DigitalOutput digitalOutput0 = new DigitalOutput();
DigitalOutput digitalOutput1 = new DigitalOutput();


            digitalOutput0.DutyCycle = 1;
digitalOutput0.Channel = 0;
digitalOutput1.Channel = 1;


             int asyncCalls = 0;
             try {
            int asyncComplete = 0;
                 digitalOutput0.Open(5000);
            double dutyCycle = 0.1;
                digitalOutput1.Open(5000);
            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);
                }


                 /*
                 await digitalOutput0.SetDutyCycleAsync(1);
                *  Do other work were
                await digitalOutput1.SetDutyCycleAsync(1);
                */
            }catch (PhidgetException ex) {
            Console.WriteLine("Failure: " + ex.Message);
             }
             }
            digitalOutput0.Close();
 
        }
//Wait until Enter has been pressed before exiting
    }
Console.ReadLine();
 
digitalOutput0.Close();
digitalOutput1.Close();
}
}
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 176: Line 154:
#include <stdio.h>
#include <stdio.h>


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


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


    PhidgetDigitalOutput_create(&digitalOutput0);
PhidgetDigitalOutput_create(&digitalOutput0);
PhidgetDigitalOutput_create(&digitalOutput1);


    Phidget_openWaitForAttachment((PhidgetHandle)digitalOutput0, 5000);
Phidget_setChannel((PhidgetHandle)digitalOutput0, 0);
Phidget_setChannel((PhidgetHandle)digitalOutput1, 1);


    PhidgetDigitalOutput_setDutyCycle(digitalOutput0, 1);
Phidget_openWaitForAttachment((PhidgetHandle)digitalOutput0, 5000);
Phidget_openWaitForAttachment((PhidgetHandle)digitalOutput1, 5000);


     asyncComplete = 0;
     PhidgetDigitalOutput_setState_async(digitalOutput0, 1, setStateDone, NULL);
    asyncCalls = 0;
PhidgetDigitalOutput_setState_async(digitalOutput1, 1, setStateDone, NULL);
    while (1) { //Some loop condition


        //Here we check if the previous asynchronous call has completed before issuing a new one
//Wait until Enter has been pressed before exiting
        //If you had multiple asynchronous calls to make, you could (for example) give each its
getchar();
        //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);
Phidget_close((PhidgetHandle)digitalOutput1);


    }
PhidgetDigitalOutput_delete(&digitalOutput0);
    Phidget_close((PhidgetHandle)digitalOutput0);
PhidgetDigitalOutput_delete(&digitalOutput1);
}
</syntaxhighlight>
|-|
Javascript=
In Javascript, all Phidget calls are async.
<syntaxhighlight lang=javascript>
import * as phidget22 from 'phidget22';


    PhidgetDigitalOutput_delete(&digitalOutput0);
// Shared async error handler wrapper
async function errHandler(actionName, fn) {
try {
return await fn();
} catch (err) {
console.error(`Error during ${actionName}`, err);
process.exit(1);
}
}
}
const conn = new phidget22.NetworkConnection(5661, 'localhost');
await errHandler('connect', () => conn.connect());
const digitalOutput0 = new phidget22.DigitalOutput();
const digitalOutput1 = new phidget22.DigitalOutput();
digitalOutput0.setChannel(0);
digitalOutput1.setChannel(1);
await errHandler('open', () =>
Promise.all([
digitalOutput0.open(5000),
digitalOutput1.open(5000),
])
);
await errHandler('setState (ch0)', () => digitalOutput0.setState(1));
await errHandler('setState (ch1)', () => digitalOutput1.setState(1));
setTimeout(async () => {
await digitalOutput0.close();
await digitalOutput1.close();
conn.close();
conn.delete();
process.exit();
}, 5000);
</syntaxhighlight>
</syntaxhighlight>
</tabber>
</tabber>

Latest revision as of 23:27, 4 March 2026

What Are Asynchronous Methods?

Asynchronous methods allow you to send commands to a device without waiting for return codes.


When calling a typical non-asynchronous method, your program must wait for the return code, blocking the rest of your program’s operation.

Async methods typical flow.png


When using an asynchronous method, your program can immediately continue executing and handle the return code in an asynchronous handler.

Async methods async flow.png

When Should I Use Asynchronous Methods?

There are two situations where asynchronous methods may be beneficial:

  1. When sending commands to multiple channels on the same device
  2. When sending commands to multiple channels on different devices

Multiple Channels Example

PhidgetLED-64 Advanced(1032_0)

The PhidgetLED-64 Advanced features 64 channels, one for each LED. To turn all the LEDs on or off simultaneously, 64 sequential commands are necessary (one for each channel). In this situation, using asynchronous methods can drastically improve the performance of your application.

Multiple Devices Example

The Isolated 12-bit Voltage Output Phidget is a single-channel device. If two or more of these devices were used in an application, it may be beneficial to use asynchronous methods.

How Do I Use Asynchronous Methods?

Asynchronous methods are comprised of two parts:

  • Asynchronous API call
  • Asynchronous handler

When calling an asynchronous method, you must assign an asynchronous handler. The Phidget22 libraries will then initiate the command and immediately return. The asynchronous handler will be called once the relevant communications have been completed to provide the return code from the device.

Code Example

This code sample shows how you can simultaneously change the state of two outputs on a device like the OUT1100. Without async, there would be a noticeable delay between the first and second output being set.

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

def main():
	
	def AsyncResult(ch, res, details):
		if res != ErrorCode.EPHIDGET_OK:
			print("Async failure: %i : %s" % (res, details))
		
	digitalOutput0 = DigitalOutput()
	digitalOutput1 = DigitalOutput()

	digitalOutput0.setChannel(0)
	digitalOutput1.setChannel(1)

	digitalOutput0.openWaitForAttachment(5000)
	digitalOutput1.openWaitForAttachment(5000)

	digitalOutput0.setState_async(True, AsyncResult)
	digitalOutput1.setState_async(True, AsyncResult)

	try:
		input("Press Enter to Stop\n")
	except (Exception, KeyboardInterrupt):
		pass

	digitalOutput0.close()
	digitalOutput1.close()

main()

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

class Java_Example {

    public static AsyncListener digitalOutputAsyncListener() {
        return new AsyncListener() {
            public void onAsyncCallback(AsyncResult ar) {
                if (ar.getReturnCode() != ErrorCode.SUCCESS)
                    System.out.println("Async Failure: " + ar);
            }
        };
    }

	public static void main(String[] args) throws Exception {
		DigitalOutput digitalOutput0 = new DigitalOutput();
		DigitalOutput digitalOutput1 = new DigitalOutput();

		digitalOutput0.setChannel(0);
		digitalOutput1.setChannel(1);

		digitalOutput0.open(5000);
		digitalOutput1.open(5000);

		digitalOutput0.setState(true, digitalOutputAsyncListener());
		digitalOutput1.setState(true, digitalOutputAsyncListener());

		//Wait until Enter has been pressed before exiting
		System.in.read();

		digitalOutput0.close();
		digitalOutput1.close();
	}
}

using System;
using Phidget22;

namespace ConsoleApplication
{
	class Program
	{

		static async void Main(string[] args)
		{
			DigitalOutput digitalOutput0 = new DigitalOutput();
			DigitalOutput digitalOutput1 = new DigitalOutput();

			digitalOutput0.Channel = 0;
			digitalOutput1.Channel = 1;

            try {
                digitalOutput0.Open(5000);
                digitalOutput1.Open(5000);

                await digitalOutput0.SetDutyCycleAsync(1);
                await digitalOutput1.SetDutyCycleAsync(1);
            }catch (PhidgetException ex) {
	            Console.WriteLine("Failure: " + ex.Message);
            }

			//Wait until Enter has been pressed before exiting
			Console.ReadLine();

			digitalOutput0.Close();
			digitalOutput1.Close();
		}
	}
}

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

void CCONV setStateDone(PhidgetHandle phid, void *ctx, PhidgetReturnCode res) {
	if(res != 0) {
        printf("setState_async Error")
    }
}

int main() {
	PhidgetDigitalOutputHandle digitalOutput0;
	PhidgetDigitalOutputHandle digitalOutput1;

	PhidgetDigitalOutput_create(&digitalOutput0);
	PhidgetDigitalOutput_create(&digitalOutput1);

	Phidget_setChannel((PhidgetHandle)digitalOutput0, 0);
	Phidget_setChannel((PhidgetHandle)digitalOutput1, 1);

	Phidget_openWaitForAttachment((PhidgetHandle)digitalOutput0, 5000);
	Phidget_openWaitForAttachment((PhidgetHandle)digitalOutput1, 5000);

    PhidgetDigitalOutput_setState_async(digitalOutput0, 1, setStateDone, NULL);
	PhidgetDigitalOutput_setState_async(digitalOutput1, 1, setStateDone, NULL);

	//Wait until Enter has been pressed before exiting
	getchar();

	Phidget_close((PhidgetHandle)digitalOutput0);
	Phidget_close((PhidgetHandle)digitalOutput1);

	PhidgetDigitalOutput_delete(&digitalOutput0);
	PhidgetDigitalOutput_delete(&digitalOutput1);
}

In Javascript, all Phidget calls are async.

import * as phidget22 from 'phidget22';

// Shared async error handler wrapper
async function errHandler(actionName, fn) {
	try {
		return await fn();
	} catch (err) {
		console.error(`Error during ${actionName}`, err);
		process.exit(1);
	}
}

const conn = new phidget22.NetworkConnection(5661, 'localhost');

await errHandler('connect', () => conn.connect());

const digitalOutput0 = new phidget22.DigitalOutput();
const digitalOutput1 = new phidget22.DigitalOutput();

digitalOutput0.setChannel(0);
digitalOutput1.setChannel(1);

await errHandler('open', () =>
	Promise.all([
		digitalOutput0.open(5000),
		digitalOutput1.open(5000),
	])
);

await errHandler('setState (ch0)', () => digitalOutput0.setState(1));
await errHandler('setState (ch1)', () => digitalOutput1.setState(1));

setTimeout(async () => {
	await digitalOutput0.close();
	await digitalOutput1.close();
	conn.close();
	conn.delete();
	process.exit();
}, 5000);

Other Considerations

Overloading

If asynchronous methods are called faster than they can be dispatched, they will be added to a queue. In extreme cases, if the queue grows large enough, newer calls will be dropped and your async handler will complete with Phidget Error 0x10 NoSpace.

Single Device, Single Channel

Asynchronous commands are not recommended when using a single device and a single channel. Multiple asynchronous calls to the same channel will be queued and issued one at a time as return codes are received for each, providing no benefit to your application.

Network Delays

If you are accessing your device(s) over a network connection with high latency, you may benefit from using asynchronous methods.

Callback Handlers

When programming using the C language, a callback handler is required. If a callback handler is not provided, the method will not run asynchronously. In all other programming languages, a callback handler is recommended but optional.

VINT Communication Speed

Asynchronous methods are particularly useful on VINT devices with lower communication speeds. If you are working with USB devices or higher-speed VINT devices, the impact of asynchronous commands will be less significant.