Using Events

From Phidgets Support
Revision as of 16:24, 9 April 2019 by Mparadis (talk | contribs)
 Phidget Programming Basics: Using EventsTOC Icon.png Table of Contents

Nav Back Arrow.png Nav Back Hover.png WhiteTab1.png HoverTab1.jpg WhiteTab2.png HoverTab2.jpg WhiteTab3.png HoverTab3.jpg WhiteTab4.png HoverTab4.jpg WhiteTab5.png HoverTab5.jpg WhiteTab6.png HoverTab6.jpg WhiteTab7.png HoverTab7.jpg WhiteTab8.png HoverTab8.jpg WhiteTab9.png HoverTab9.jpg GreenTab10.png WhiteTab11.png HoverTab11.jpg WhiteTab12.png HoverTab12.jpg WhiteTab13.png HoverTab13.jpg WhiteTab14.png HoverTab14.jpg WhiteTab15.png HoverTab15.jpg WhiteTab16.png HoverTab16.jpg Nav Next Arrow.png Nav Next Hover.png


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

Click to Enlarge

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 onStateChangeHandler(StateChangeEvent e) {
		System.out.println("State: " + e.getState());
    }
};

public static DigitalInputStateChangeListener aDifferentStateChange = new DigitalInputStateChangeListener() {
    @Override
    public void onStateChangeHandler(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 the event handlers
function stateChange(state) {
    console.log('State: ' + state)
}

function aDifferentStateChange(state) {
    console.log('A Different StateChange Handler')
    if(state) {
	    // Do something
    }
    else {
        // Do something else
    }	
}
...
// Declare your objects
var oneChannel = new phidget22.DigitalInput()
var anotherChannel = new phidget22.DigitalInput()
...
// Assign the handlers that will be called when the event occurs
oneChannel.StateChange += stateChange;
anotherChannel.StateChange += aDifferentStateChange;

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....

In C, all event handler declarations have a context pointer that can be pointed at any object you choose. This can be a set of relevant data, or even a Phidget handle.

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...

}

JavaScript 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 may access the information using the 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;
}
...
    var 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;

    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.