Using Events
10 . Using Events
If you are new to Phidgets, or event-driven programming, chances are your first instinct will be to poll for everything. Polling is the action of repeatedly checking a variable or property to see if it has changed, and is generally done in-line from the main body of your application. While this can be effective in smaller applications, this quickly becomes inefficient. You also risk missing a change your program is waiting for, if it polls for data too slowly.
Events
The Phidget library allows for the use of event-driven programming. This allows your program to react to certain stimuli as they happen, rather than having to constantly check for changes. By setting up event handlers, you are setting up functions that run every time the event associated with them occurs. Once they have been assigned, these functions run independently from the main body of code. For example, a State Change event handler for a DigitalInput
channel will run every time the input state changes. Similarly, Attach and Detach events will occur when a device channel attaches to and detaches from your program (e.g. is plugged in and unplugged).
Using events can be very useful for tasks such as recording every data point from a Phidget sensor, reacting to a button being pressed, or initializing a channel as soon as it is attached. This frees up the main body of your program from having to constantly check the status of the Phidget, which can significantly clean up many applications.
Special Considerations
When using events, you must be mindful of keeping the code handling the event running as quickly as possible. If the event handler takes longer than the interval between events, this will cause the events to pile up and fall out of sync with the time they occur, and eventually start being lost.
When using events, you will need to be careful about what you share between the event and your main code. This is because events can happen at any time, so any variables shared with them could change unexpectedly.
Recommendations
We recommend all but the simplest program should set handlers that will be called when a channel attaches, detaches, sends an error, or receives new sensor data.
Initializing a channel should be done from the attach event handler, so it will always be configured as intended, even if something causes it to be unplugged and plugged back in.
Error Events
Make sure not to ignore using the Phidget error event handler, as many Phidgets will send critical information to the program using error events, such as when a sensor takes a measurement outside of its valid range, or activates some safety feature.
Using Multiple Event Handlers
When using events with multiple channels of the same type, you can either handle them with the same event handler, or create separate events for each channel (or some combination of both).
It can simplify your application greatly to assign specific event handlers to different channels. This can be done as follows:
#Declare the event handlers
def onStateChangeHandler(self, state):
print("OnStateChangeHandler: " + state)
def aDifferentStateChangeHandler(self, state):
print("A Different StateChange Handler")
if(state):
#do something
else:
#do something else
...
#Declare your objects. Replace "DigitalInput" with the object for your Phidget
oneChannel = DigitalInput()
anotherChannel = DigitalInput()
...
#Assign the handlers that will be called when the respective events occur
oneChannel.setOnStateHandler(onStateChangeHandler)
anotherChannel.setOnStateHandler(aDifferentStateChangeHandler)
//Declare the event listeners
public static DigitalInputStateChangeListener onStateChange = new DigitalInputStateChangeListener() {
@Override
public void onStateChange(StateChangeEvent e) {
System.out.println("State: " + e.getState());
}
};
public static DigitalInputStateChangeListener aDifferentStateChange = new DigitalInputStateChangeListener() {
@Override
public void onStateChange(StateChangeEvent e) {
System.out.println("A Different StateChange Handler");
if(e.getState()) {
//Do something
}
else {
//Do something else
}
}
};
...
//Declare your object. Replace "DigitalInput" with the object for your Phidget.
DigitalInput oneChannel;
DigitalInput anotherChannel;
...
//Assign the event listeners that will be called when the event occurs
oneChannel.addStateChangeListener(onStateChange);
anotherChannel.addStateChangeListener(aDifferentStateChange);
//Declare the event handlers
void stateChange(object sender, Phidget22.Events.StateChangeEventArgs e) {
Console.WriteLine("State: " + e.State.ToString());
}
void aDifferentStateChange(object sender, Phidget22.Events.StateChangeEventArgs e) {
Console.WriteLine("A Different StateChange Handler");
if(e.State) {
//Do something
}
else {
//Do something else
}
}
...
//Declare your objects
DigitalInput oneChannel;
DigitalInput anotherChannel;
...
//Assign the handlers that will be called when the event occurs
oneChannel.StateChange += stateChange;
anotherChannel.StateChange += aDifferentStateChange;
//Declare the event handlers
static void CCONV onStateChangeHandler(PhidgetDigitalInputHandle ph, void *ctx, int state) {
printf("State: %d\n", state);
}
static void CCONV aDifferentStateChangeHandler(PhidgetDigitalInputHandle ph,
void *ctx, int state) {
printf("A Different StateChange Handler");
if(state) {
//Do something
}
else {
//Do something else
}
}
...
//Declare your channels
PhidgetDigitalInputHandle oneChannel;
PhidgetDigitalInputHandle anotherChannel;
...
//Assign the handlers that will be called when the event occurs
PhidgetDigitalInput_setOnStateChangeHandler(oneChannel, onStateChangeHandler, NULL);
PhidgetDigitalInput_setOnStateChangeHandler(anotherChannel, onStateChangeHandler, NULL);
// Declare your objects
const oneChannel = new phidget22.DigitalInput()
const anotherChannel = new phidget22.DigitalInput()
...
// Define the handlers that will be called when the event occurs
oneChannel.StateChange = function(state) {
console.log('State: ' + state)
}
anotherChannel.StateChange = function(state) {
console.log('A Different StateChange Handler')
if(state) {
// Do something
}
else {
// Do something else
}
}
Linking Data to Events
When you're using events in your program, it may become useful to link certain information to the event for a given Phidget channel. Some programming languages provide a convenient way of doing exactly that, though the specifics depend on the programming language you are using:
Python is dynamically interpreted, and objects follow a less rigid structure than in other languages. To link a variable with a given Phidget object to have it available from the event, you can add it to the Phidget object that will be triggering the event. Then, you can access the information using the self
parameter of the event.
For example, if we wanted to group a number with a Phidget channel to be used in an event:
def onStateChangeHandler(self, state):
#We can now access and even change "myVariable" from the event
print(self.myVariable)
self.myVariable += 1
ch = DigitalInput()
#Addressing info here
#Here we create an attribute of ch called "myVariable", and assign it the information to store
#We'll use an integer here for simplicity, but this could be anything, even a second Phidget Handle
ch.myVariable = 0
ch.setOnStateChangeHandler(onStateChangeHandler)
ch.openWaitForAttachment(5000)
# The rest of your code here....
For example, if we wanted to link a number with a specific event for a Phidget channel:
static void CCONV onStateChangeHandler(PhidgetDigitalInputHandle pdih, void *ctx, int state) {
//We can now access the information at the pointer from the event
int* myIntPtr = (int*)ctx;
printf("Int: %d\n", *myIntPtr);
(*myIntPtr) ++;
}
int main() {
PhidgetDigitalInputHandle ch = NULL;
int myIntMain = 0;
PhidgetDigitalInput_create(&ch);
//Addressing info here
//Here we pass "myStringMain" as the context pointer so we can access it from the event
//This can be a pointer to any variable or structure, or even a second Phidget handle
PhidgetDigitalInput_setOnStateChangeHandler(ch, onStateChangeHandler, &myIntMain);
Phidget_openWaitForAttachment((PhidgetHandle)ch, 5000);
//The rest of your code here...
}
this
parameter of the event.
For example, if we wanted to group a number with a Phidget channel to be used in an event:
function stateChange(state) {
//We can now access and even change "myVariable" from the event
console.log(this.myVariable);
this.myVariable += 1;
}
...
const ch = new phidget22.DigitalInput();
...
//Here we create a property of ch called "myVariable", with the information to store
//We'll use an integer here for simplicity, but this could be anything,
//even a second Phidget Handle
ch.myVariable = 0
ch.onStateChange = stateChange;
await ch.open();
//The rest of your code here...
When should I poll?
If all you need is a simple program that reads a small amount of data from a device, it is possible to open a channel, wait for attachment, read the data, and close the channel. In that case, the code to implement and set event handlers might not be worth the effort. When polling, error checking becomes very important, as accessing some channel properties while the channel is detached, or before any data comes back from the Phidget, will result in an error or exception that could kill the program. There will always be a race between checking the Attached
property and accessing a data or state property.
When in doubt, use event handlers.