Setting Up Your Own Objects

Overview

Creating a PPS object is as easy as making an open() call on a file under /pps with the O_CREAT flag, which will create the PPS object if it doesn't already exist. Opening, closing, reading from, and writing to PPS objects will use the exact same mechanisms as opening, closing, reading from, and writing to files on the filesystem. As shown in "Overview of the PPS Service" in this guide, as long as the data you write conforms to the format PPS expects, you can write anything to your PPS objects.

Note: We recommend that you use the libpps API for encoding/decoding PPS data. These library functions make handling data easier, faster, and more reliable than using standard libc functions. For more information, see "PPS API reference" in the QNX Persistent Publish/Subscribe Developer's Guide.

Guidelines

You could design your program to interact with PPS objects in any variety of ways. Your design will include decisions such as whether to read objects in delta mode, how frequently to read, what data to write, whether or not you receive notifications in the form of pulses, and so on. Even more decisions come into play if you're designing a system that communicates through PPS using server objects.

Here are the basic steps for setting up your own PPS objects, whether you're designing a program that interacts with PPS objects or adding that capability to an existing program:
  1. Make sure your program includes the <fcntl.h> and <sys/pps.h> header files.
  2. Open the PPS object as if it were a file. For example, to make an open call on an existing object:

    open("/pps/myobject", O_RDWR);

    This will open myobject with read and write privileges.

    If you're creating a PPS object that doesn't already exist, include the O_CREAT flag:

    open("/pps/an-object", O_RDWR | O_CREAT);

    Here we're including both O_RDWR and O_CREAT in one field with the bitwise OR operation.

  3. If you need to make a new directory, you can use the mkdir() function. For example, to create a directory called myservice under /pps/services/:

    mkdir("/pps/services/myservice", S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);

    This will make your new directory with read and write privileges for all users.

  4. Now you probably want to perform a read or write. Remember to use the pps_encoder_*() and pps_decoder_*() functions for handling your data.
  5. Eventually you'll need to close the PPS object before your program terminates.

Interacting with your PPS objects

The basic "building blocks" you'll use for interacting with PPS objects are relatively few:

But you'll find many possibilities of combining these together, combining them with synchronization techniques (mutual exclusion locks, condition variables, etc.), and employing various ways to perform the same tasks. Again, see the QNX Persistent Publish/Subscribe Developer's Guide for guidance.

How you'll use mutexes and other synchronization tools is up to you and depends on the needs of your program. As you'll see in the "gears" example below, mutexes are used to ensure coherency between two parallel threads: one is reading new data from PPS while the other is using existing data to draw the gears. In this case, mutexes are used so that one thread doesn't try to change attributes that the other thread is trying to use. Naturally, the synchronization needs of your programs may be different.

OpenGL "gears" example

The QNX CAR platform includes the popular OpenGL ES 2.0 "gears" demo, which runs as a sample app in the HMI. Besides providing a useful example of a 3D graphics program on an embedded system, our version of the gears demo also supports PPS. The source code is available, so you can use it as a reference for building your own PPS objects.

We have modified the gears program to create two PPS objects:

The control object lets you change the size of the window, the speed of the gears, and other properties, while the status object publishes the animation frame rate.

SVN repository for demo source

The source code for the gears demo projects is available here:

http://community.qnx.com/svn/repos/qnxcar2_platform/2.0/src/sample/

Under the sample directory you'll find three subdirectories for different versions of the gears program:

Directory Description
gles2-gears The classic OpenGL gears demo for embedded systems. The demo draws the animated gears on the screen and prints the frame rate.
gles2-gears-pps The same gears demo, but modified for PPS. The program creates and uses the /pps/services/gears/control and /pps/services/gears/status objects.
gles2-gears-pps-html The same PPS version of the demo, but with HTML5 support. The HTML5 elements show how your HMI can write to the control object and see the changes on the screen.

Note that the gles2-gears-pps-html program contains a C-language part as well as a WebWorks part, which is located here:

http://community.qnx.com/svn/repos/qnxcar2_platform/2.0/html5/webworks/apps/HTMLGears/
Note: For information on developing WebWorks apps, see Developing HTML5 Apps with WebWorks.

The gears.h header for gles2-gears-pps

Let's start with gears.h, a header file for the gles2-gears-pps demo. We set up this header for these key operations:
  1. Include <sys/pps.h> to provide access to functions in the libpps library.
  2. Define PPS_DIR for the directory for the status and control objects (/pps/services/gears/).
  3. Define PPS_STATUS_PATH for the status object (/pps/services/gears/status). The path to the control object (/pps/services/gears/control) is defined as a macro in the pps.c file. Note that the file descriptors for the status and control objects are saved as global variables after the files are opened. The status object is opened in the pps_init() function; the control object is opened in the ctrl_pps_monitor_thread() function.
  4. Create a macro for writing to the status and control objects:
    #define write_pps_buf(buf, strFmt, fd, val)\
    	flushall();\
    	sprintf(buf, strFmt, val);\
    	write(fd, buf, strlen(buf));\
    	flushall();
    
  5. List the attributes used in the control object:
    enum {
    	PPS_GEARS_ATTR_OBJNAME,
    	PPS_GEARS_ATTR_INTERVAL,
    	PPS_GEARS_ATTR_PAUSE,
    	PPS_GEARS_ATTR_SCREEN_GROUP,
    	PPS_GEARS_ATTR_ACTIVATED,
    	PPS_GEARS_ATTR_X,
    	PPS_GEARS_ATTR_Y,
    	PPS_GEARS_ATTR_W,
    	PPS_GEARS_ATTR_H,
    	PPS_GEARS_ATTR_Z,
    	PPS_GEARS_ATTR_LAST
    }
    
  6. Set up a mutex to coordinate access to the interval variable (for the swap interval):

    pthread_mutex_t pps_mutex;

  7. Set up condition variables:
    //For waiting for valid screen group
    	pthread_cond_t		screenGroup_cond;
    	
    //For waiting to be activated
    	pthread_cond_t		activated_cond;
    	
    //For pause/unpause
    	pthread_cond_t		pause_cond;
    
  8. Set up file handles:
    int fd_pps_status;
    int fd_pps_ctrl;
    
  9. Set up these functions (implemented in pps.c) for access by other files:
    // pps.c
    extern void *ctrl_pps_monitor_thread(void * arg);
    extern void pps_init(gears_t *gr);
    

The pps.c file for gles2-gears-pps

As mentioned previously, the pps.c file sets up the path to the control object:

#define PPS_CTRL_PATH "/pps/services/gears/control?wait,delta"

The pps.c file also sets up these functions:
pps_init()
Creates and opens the status object for publishing the frame rate. If the object doesn't already exist, PPS creates it before the open() call returns. The pps_init() function also spawns a thread to run the ctrl_pps_monitor_thread() function.
ctrl_pps_monitor_thread()
Forever looping, this function opens the /pps/services/gears/control object, reads from it, and changes the demo's global settings variables in response to changes in the object. If the control object doesn't already exist, PPS creates it before the open() call returns. Note that the control object is opened with the ?wait and ?delta flags (together as ?wait,delta). This is done so that the read() call will block until a change is made to the object since the last read, thus returning only the attributes that have changed.
psparse()
A custom function to tokenize attributes read from a PPS object.
Note: The libpps library includes a set of encoder/decoder functions, so you won't need to write your own parsing functions. For details, see "PPS encoding and decoding API" in the QNX Persistent Publish/Subscribe Developer's Guide.

The gles2-gears.c file for gles2-gears-pps

We modified the original gles2-gears.c file for PPS support as follows:
  1. The main(int argc, char *argv[]) function calls the pps_init() function. The main thread then loops over the functionality outlined in the following steps. Note that this loop isn't strictly due to PPS (a draw loop is needed in any case).
  2. The main thread waits for a window group to be set in the ctrl_pps_monitor_thread() function with a value read from the control object.
  3. The main thread attempts to join the window group. If this fails, it publishes a value of "" (the empty string) for the screenGroup attribute in the control object.
  4. If the main thread detects that the pause variable is set to true (1), the thread waits on a condvar until ctrl_pps_monitor_thread() signals and updates pause to false.
  5. The main thread continues to read global parameters that could have been changed by ctrl_pps_monitor_thread() and then updates its own working variables based on them. If the main thread encounters an error while assigning updated global variables to its local copies, it will publish the attributes and values that it's using (instead of the new values) to the control object.
  6. The main thread then draws the gears (which has nothing to do with PPS).
  7. The main thread swaps draw buffers. If there's an error, the thread publishes a value of "" (the empty string) for screenGroup in the control object.
  8. Finally, the main thread publishes the frame rate to the status object, using the rate calculated from the last draw cycle.

HTMLGears

The HTMLGears app is the HTML5 version of our gears demo. HTMLGears includes these JavaScript files:

The gears.js file deals with the PPS activities of the HTMLGears demo. The code is as follows:
var _pps = require("lib/pps/ppsUtils"),
    _gearsCtrlPPS;

/**
 * Exports are the publicly accessible functions
 */
module.exports = {

    init: function () {
        _gearsCtrlPPS = _pps.createObject();
        _gearsCtrlPPS.init();
        _gearsCtrlPPS.open("/pps/services/gears/control", JNEXT.PPS_WRONLY);
    },
    
    /**
     * Sets the parameters for the Gears application
     */
    setParams: function(args) {
        // otherwise uses application defaults
        _gearsCtrlPPS.write({x:args.x, y:args.y, w:args.w, h:args.h});
        
        // otherwise uses no screen group and appears fullscreen,
        // on top of all other windows
        if (typeof args.screenGroup != 'undefined') {
            _gearsCtrlPPS.write({screenGroup:args.screenGroup});
        } else {
            rc = false;
        }
        
        return rc;
    },

    /**
     * Writes the activation command to the pps object
     * 
     */
    start: function() {
        _gearsCtrlPPS.write({activated:1});
    },

    /**
     * Writes the pause command to the pps object
     * 
     */
    stop: function() {
        _gearsCtrlPPS.write({activated:0});
    }
};
Let's look at the key parts:
  • The file links to the PPS utilities file ppsUtils.js (which resides under the Framework/lib/ directory). This file defines the PPS-related functions for the JavaScript interface.
  • These lines create and initialize a JavaScript object that can then interact with the PPS control object:
    _gearsCtrlPPS = _pps.createObject();
    _gearsCtrlPPS.init();
    
  • The JavaScript object opens /pps/services/gears/control as a write-only object.
  • The setParams: function(args) function sets the window parameters (x dimension, y dimension, width, height, and screenGroup). When the HTMLGears app runs, the Window Group field in the display will show the screenGroup value (e.g., 1-2412607-BlackBerry-window-group).
  • These lines:
    _gearsCtrlPPS.write({activated:1});
    _gearsCtrlPPS.write({activated:0});
    
    will write the values for the activated attribute to the PPS control object. In the HTMLGears app, the Start and Stop buttons reflect the activated:n:1 and activated:n:0 values shown in the control object. Here's a sample control object:
    # cat /pps/services/gears/control
    @control
    activated:n:1
    h:n:395
    screenGroup::1-2412607-BlackBerry-window-group
    w:n:800
    x:n:0
    y:n:0
    #
    
Here's the basic pattern for creating a PPS object in JavaScript:
this.applicationPPS = new JNEXT.PPS();
this.applicationPPS.init();
this.applicationPPS.open(APPLICATION_PPS, "4");