next up previous contents index
Next: Image Display Tool Up: Karma Programming Manual Previous: Widget Library

Advanced Examples

This chapter takes you through some more advanced examples, which tie together some of the concepts introduced in previous chapters.

Instrument Control

This example contains three separate programmes, one is a server and the other two are clients of the server. The server needs to periodically inform one type of client of it's current status, and also respond to requests from the other type of client with the current status. To do all this, the server needs to make use of the event dispatch facilities as well as the communications support, so this is a nice way to demonstrate how these facilities operate in harmony.

The client which receives periodic updates is called the ``wait'' client. The client which requests updates from the server is called the ``query'' client.

While the connections are managed by the conn package, when the time comes to actually transfer data, the ch package is used to do the transfers. Each connection has a channel object associated with it. Calling conn_get_channel returns the channel object.

Server

The server defines two communications protocols.

The ``periodic_data'' protocol is for clients who wish to receive periodic updates: in other words, the server decides when to send the data. Since the clients will not be sending any data to the server, there is no need to register any callback functions. Because the server needs to periodically send data, the e package is used to periodically run an event function (periodic_func). This function is called when the server wants to send data. It simply loops over the ``periodic_data'' connections, sending data to each in turn.

The other protocol is the ``query_data'' protocol. Here the server resonds to a request from a client and sends the status information back; the client decides when data should be sent. Since the server is responding to client queries, a read callback needs to be registered for this protocol. This is the read_func.

Note that the server does not have to worry about the mechanics of waiting for connections, or detecting when they are closed. The server can run indefinately, with clients connecting and disconnecting at will, with no extra effort required. All the boring bits are handled by the library.

/*-----------------------------------------------------------*/
/*  Communications sample program: instrument control server */
/*-----------------------------------------------------------*/

#include <stdio.h>
#include <karma_conn.h>
#include <karma_dm.h>
#include <karma_ch.h>
#include <karma_r.h>
#include <karma_e.h>


#define PORT_NUMBER 10000


STATIC_FUNCTION (flag periodic_func, (KPeriodicEventFunc func, void *info) );
STATIC_FUNCTION (flag read_func, (Connection connection, void **info) );


void main ()
{
    KPeriodicEventList event_list;
    unsigned int port_number = PORT_NUMBER;

    /*  Initialise communications package  */
    dm_native_setup ();
    conn_initialise ( ( void (*) () ) NULL );
    /*  Register server protocols  */
    conn_register_server_protocol ("periodic_data", 0, 0,
                                   ( flag (*) () ) NULL, ( flag (*) () ) NULL,
                                   ( void (*) () ) NULL);
    conn_register_server_protocol ("query_data", 0, 0,
                                   ( flag (*) () ) NULL, read_func,
                                   ( void (*) () ) NULL);
    /*  Allocate a port  */
    if ( !conn_become_server (&port_number, 0) )
    {
        fprintf (stderr, "Could not become a server\n");
        exit (1);
    }
    fprintf (stderr, "Port number: %u\n", port_number);
    event_list = e_unix_create_list (0, 1, NULL);
    e_register_func (event_list, periodic_func, NULL, 1, DISPATCH_SYNCHRONOUS);
    /*  Enter polling loop (forever)  */
    while (TRUE) dm_native_poll (-1);
}   /*  End Function main  */


/*  Private functions follow  */

static flag periodic_func (KPeriodicEventFunc func, void *info)
/*  [SUMMARY] Event callback.
    [PURPOSE] This routine is called when a periodic event occurs.
    <func> The KPeriodicEventFunc object.
    <info> A pointer to arbitrary information.
    [RETURNS] TRUE if the event function should be called again, else FALSE.
*/
{
    Connection conn;
    Channel ch;
    char dummy;
    unsigned int count, num_conn;

    num_conn = conn_get_num_serv_connections ("periodic_data");
    for (count = 0; count < num_conn; ++count)
    {
        conn = conn_get_serv_connection ("periodic_data", count);
        ch = conn_get_channel (conn);
        ch_write (ch, &dummy, 1);
        ch_flush (ch);
    }
    return (TRUE);
}   /*  End Function periodic_func  */

static flag read_func (Connection connection, void **info)
/*  [SUMMARY] Connection read event callback.
    [PURPOSE] This routine is called when data is ready to be read from a
    connection.
    <connection> The connection object.
    <info> A pointer to the arbitrary information pointer. This may be modified
    [RETURNS] TRUE on successful reading, else FALSE (indicating the connection
    should be closed).
    [NOTE] The <<close_func>> will be called if this routine returns FALSE.
*/
{
    Channel ch;
    char dummy;

    ch = conn_get_channel (connection);
    ch_read (ch, &dummy, 1);
    ch_write (ch, &dummy, 1);
    ch_flush (ch);
    return (TRUE);
}   /*  End Function read_func  */

Note how calls are made to ch_flush. If this is not called, data is not actually sent until the channel buffer fills, and that could be a long time later (especially in this simple example where the ``status data'' being sent to the clients is just 1 byte).

Wait Client

This programme only needs to register support for the ``periodic_data'' protocol, supplying a read callback (the read_func function) which is called when the server has sent some new data. Apart from reading the data from the server, the read_func function also prints the time elapsed since the first piece of data was received from the server. Since this should occur every second, the delay relative to the nearest integer second is also displayed.

The programme is run with the first argument being the host where the server is running.

/*----------------------------------------------------------*/
/*  Communications sample program:  wait client             */
/*----------------------------------------------------------*/

#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/resource.h>
#include <karma_conn.h>
#include <karma_dm.h>
#include <karma_ch.h>
#include <karma_r.h>


#define PORT_NUMBER 10000


STATIC_FUNCTION (flag read_func, (Connection connection, void **info) );


void main (int argc, char **argv)
{
    /*  Initialise communications package  */
    dm_native_setup ();
    conn_initialise ( ( void (*) () ) NULL );
    /*  Register server protocols  */
    conn_register_client_protocol ("periodic_data", 0, 0,
                                   ( flag (*) () ) NULL, ( flag (*) () ) NULL,
                                   read_func, ( void (*) () ) NULL);
    /*  Connect to server  */
    if ( !conn_attempt_connection (argv[1], PORT_NUMBER, "periodic_data") )
    {
        fprintf (stderr, "Could not connect to server\n");
        exit (1);
    }
    /*  Enter polling loop (forever)  */
    while (TRUE) dm_native_poll (-1);
}   /*  End Function main  */


/*  Private functions follow  */

static flag read_func (Connection connection, void **info)
/*  [SUMMARY] Connection read event callback.
    [PURPOSE] This routine is called when data is ready to be read from a
    connection.
    <connection> The connection object.
    <info> A pointer to the arbitrary information pointer. This may be modified
    [RETURNS] TRUE on successful reading, else FALSE (indicating the connection
    should be closed).
    [NOTE] The <<close_func>> will be called if this routine returns FALSE.
*/
{
    Channel ch;
    char dummy;
    double d_time;
    struct timeval curr_time;
    static double old_time = 0.0;
    static struct timezone tz = {0, 0};

    ch = conn_get_channel (connection);
    ch_read (ch, &dummy, 1);
    gettimeofday (&curr_time, &tz);
    d_time = curr_time.tv_sec;
    d_time += (double) curr_time.tv_usec * 1e-6;
    if (old_time < 1e-6) old_time = d_time;
    d_time -= old_time;
    fprintf (stderr, "%f s\t%f.2 us\n",
             d_time, ( d_time - rint (d_time) ) * 1e6);
    return (TRUE);
}   /*  End Function read_func  */

Query Client

This programme only needs to register support for the ``query_data'' protocol. Since the programme has nothing else to do while it's waiting for the server to respond to a request, no callbacks are needed. Instead, a request is sent to the server and the programme reads (and blocks), waiting for the response from the server. To make it a bit more interesting, this sequence is repeated in a simple loop. As with the wait client, the times are also displayed.

The programme is run with the first argument being the host where the server is running. The second argument is the number of iterations in the loop.

/*----------------------------------------------------------*/
/*  Communications sample program:  query client            */
/*----------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/resource.h>
#include <karma_conn.h>
#include <karma_dm.h>
#include <karma_ch.h>
#include <karma_r.h>


#define PORT_NUMBER 10000


void main (int argc, char **argv)
{
    Connection connection;
    Channel ch;
    char dummy;
    unsigned int num_iter, count;
    double d_time, reltime;
    static struct timezone tz = {0, 0};
    struct timeval curr_time;
    struct timeval old_time;

    /*  Initialise communications package  */
    dm_native_setup ();
    conn_initialise ( ( void (*) () ) NULL );
    /*  Register server protocols  */
    conn_register_client_protocol ("query_data", 0, 0,
                                   ( flag (*) () ) NULL, ( flag (*) () ) NULL,
                                   ( flag (*) () ) NULL, ( void (*) () ) NULL);
    gettimeofday (&old_time, &tz);
    /*  Connect to server  */
    if ( !conn_attempt_connection (argv[1], PORT_NUMBER, "query_data") )
    {
        fprintf (stderr, "Could not connect to server\n");
        exit (1);
    }
    connection = conn_get_client_connection ("query_data", 0);
    ch = conn_get_channel (connection);
    num_iter = atoi (argv[2]);
    for (count = 0; count < num_iter; ++count)
    {
        ch_write (ch, &dummy, 1);
        ch_flush (ch);
        ch_read (ch, &dummy, 1);
        gettimeofday (&curr_time, &tz);
        d_time = curr_time.tv_sec;
        d_time += (double) curr_time.tv_usec * 1e-6;
        reltime = (double) (curr_time.tv_sec - old_time.tv_sec) * 1e3;
        reltime += (double) (curr_time.tv_usec - old_time.tv_usec) * 1e-3;
        fprintf (stderr, "%f s\t%.2f ms\n", d_time, reltime);
    }
}   /*  End Function main  */


next up previous contents index
Next: Image Display Tool Up: Karma Programming Manual Previous: Widget Library

Richard Gooch
Wed Sep 16 16:25:36 EST 1998