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.
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.
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.
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.
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.
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.
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.
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/
#define write_pps_buf(buf, strFmt, fd, val)\ flushall();\ sprintf(buf, strFmt, val);\ write(fd, buf, strlen(buf));\ flushall();
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 }
pthread_mutex_t pps_mutex;
//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;
int fd_pps_status; int fd_pps_ctrl;
// pps.c extern void *ctrl_pps_monitor_thread(void * arg); extern void pps_init(gears_t *gr);
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 HTMLGears app is the HTML5 version of our gears demo. HTMLGears includes these JavaScript files:
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}); } };
_gearsCtrlPPS = _pps.createObject(); _gearsCtrlPPS.init();
_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 #
this.applicationPPS = new JNEXT.PPS(); this.applicationPPS.init(); this.applicationPPS.open(APPLICATION_PPS, "4");