Registering for exceptions from the Cascade DataHub

Cogent API and Utilities: Version 4.1
Prev | Appendix A. Cascade DataHub Code Examples | Next

# A.3. Registering for exceptions from the Cascade DataHub

    /*
     * Cascade DataHub point waiter:  waiter.c
     *
     * (C) Copyright Cogent Real-Time Systems Inc., 1997.  All rights reserved.
     *
     * This program registers for exceptions on all or selected points
     * in the Cascade DataHub.
     *
     * The program waits in an infinite receive loop for exceptions.
     * Upon receipt of a message the program checks that the message is
     * a Cascade DataHub exception and then parses and prints the point
     * to stdout.
     *
     * Since this program can be made to register for exceptions on multiple
     * points a linked-list facility is used to create a list of point names
     * from the passed args.  This linked list is then walked to register
     * for exceptions on each point individually.
     *
     * This program is supplied with the Cascade DataHub programming API.  It
     * may be copied or modified, in whole or in part, for the sole purpose of
     * creating applications to be used with the Cascade DataHub.
     */

    #include    <stdio.h>
    #include    <stdlib.h>
    #include    <string.h>
    #include    <unistd.h>
    #include    <time.h>
    #include    <sys/kernel.h>

    #include    <cogent/cogdb.h>

    /*
     * millisecs function converts nanoseconds (10E-9) to milliseconds (10E-3).
     */
    static int millisecs(PT_pCPOINT ppoint)
    {
        return (ppoint->nanoseconds / 1000000);
    }

    /*
     * Generate an ASCII representation of the date and time for a given
     * point, truncating the nanoseconds to the next lowest millisecond.
     * If the time values are 0,0, then generate the string "none".
     */

    static char* timeof(PT_pCPOINT ppoint)
    {
        static char    ctm[64], msec[16];
        char        *thetime;
        time_t        tm;

        if (ppoint->seconds)
        {
            tm = ppoint->seconds;
            thetime = ctime (&tm);
            strncpy (ctm, thetime, 11);
            strncpy (&ctm[11], &thetime[20], 4);
            strncpy (&ctm[15], &thetime[10], 9);
            ctm[24]='\0';
            sprintf (msec, ".%03d", millisecs(ppoint));
            strcat (ctm, msec);
        }
        else
        {
            strcpy (ctm, "none");
        }
        return (ctm);
    }

    /*
     * Print point name, value and information to the standard output
     */
    void print_point (PT_pCPOINT ppoint)
    {
        printf ("Point: %s\n", ppoint->name);
        switch (ppoint->type)
        {
          case PT_TYPE_INT32:
            printf ("Value: %d\n", ppoint->value.i);
            break;
          case PT_TYPE_REAL:
            printf ("Value: %.20g\n", ppoint->value.r);
            break;
          case PT_TYPE_STRING:
            printf ("Value: %s\n", ppoint->value.s);
            break;
          default:
            printf ("Value: Unknown\n");
            break;
        }
        printf ("Conf:  %d, Lock: %s, Time: %s, Security: %d\n",
                ppoint->conf, (ppoint->locked ? "yes" : "no"),
                timeof(ppoint),
                ppoint->security);
    }

    void Usage (char** argv)
    {
        print_usage (argv);
        exit(1);
    }

    #ifdef __USAGE
    %C [-d domain] [-q queuename] pointname...
        -d domain - set the domain for the operation
        -q        - name of queue for point changes
        -v        - print debugging information

        Points in another domain may be watched by
        texplicitly naming the domain followed by a colon
        and the point name.
            e.g.,
                waiter mixer:Mixer_1_Weight
            is the same as
                waiter -d mixer Mixer_1_Weight
    #endif

    void main (int argc, char** argv)
    {
        IP_hMSG            hmsg;
        ST_STATUS        status;
        PT_stCPOINT        point;
        char            *ptname = NULL, *qname = NULL, *domain=NULL;
        char            *s, namebuf[256], *name = NULL, *msgend;
        int                i, debugging=0;
        IP_hTASK        htask, sender;
        IP_MSG_TYPE        type;
        LL_LIST            names;
        LL_stITERATOR    it;

        hmsg = IP_CreateMsg (0, 0, NULL, IP_MAX_MESSAGE, NULL);

        /*
          * Create an empty link list
         */
        names = LL_New();

        for (i=1; i<argc; i++)
        {
            if (argv[i][0] == '-')
            {
                switch (argv[i][1])
                {
                  case 'd':
                    domain = argv[++i];
                    if (strlen(domain) > 15)
                        domain[15] = '\0';
                    break;
                  case 'q':
                    qname = argv[++i];
                    break;
                  case 'v':
                    debugging = 1;
                    break;
                  default:
                    Usage (argv);
                    break;
                }
            }
            else
            {
                /*
                 * This else clause adds the current arg to the end of the
                 * linked list
                 */
                LL_AddTail (names, argv[i]);
            }
        }

        /*
         * If a queue name is not specified then generate one based on the
         * process pid
         */
        if (!qname)
        {
            sprintf (namebuf, "sc/wait%d", getpid());
            qname = strdup (namebuf);
        }
        if ((s = strrchr (argv[0], '/')))
            s++;
        else
            s = argv[0];

        /*
         * Generate a registered name based on the process pid
         */
        if (!name)
        {
            sprintf (namebuf, "sc/wait%d", getpid());
            name = strdup (namebuf);
        }

        /*
         * Initialize this process with the name server and queue server
         */
        htask = NS_Init (name, qname, domain, 0);

        memset (&point, 0, sizeof(point));

        /*
         * Traverse the link list of points (if passed) and register for
         * exceptions on each.  Otherwise register for all exceptions.
         */
        if (LL_Count(names))
        {
            LL_TRAVERSE (names, char*, ptname, it)
            {
                printf ("Register %s\n", ptname);
                point.name = ptname;
                point.conf = 0;
                point.address = NULL;
                if ((status = DH_RegisterPoint (htask, &point, hmsg, NULL))
                    != ST_OK)
                    printf ("Register point failed: %d\n", status);
            }
        }
        else
        {
            if ((status = DH_RegisterAllPoints (htask, NULL, 1,
                                                hmsg, NULL))
    != ST_OK)
            {
                printf ("Register all points failed: %d\n", status);
                exit (-1);
            }
        }

        /*
         * Provide a point name buffer separately from the rest of the point
         * structure.  There is no way for the API to know what the allocation
         * status of a point name is, so it will never attempt to free this
         * buffer, nor write into it.
         */
        point.name = namebuf;

        /*
         * Infinite event loop
         */
        for (;;)
        {
            /*
             * Sit receive blocked
             */
            sender = IP_TaskReceive (htask, NULL, hmsg, &type, NULL);
            if (debugging)
                printf ("Received: %s\n", IP_ReadMsgBuffer (hmsg));

            /*
             * If the message is an exception notice from the Cascade DataHub
             * then parse and print the point.  We could get messages from
             * other sources as well, which we effectively ignore.  If any
             * process attempts to Send a message to this task, we just send
             * back a nil response.
             */
            switch (type)
            {
              case IP_ASYNC:            /* DataHub point, probably */
                switch (IP_MsgCommand (hmsg))
                {
                  case ST_DH_EXCEPTION:
                    /* Deal with exceptions by parsing and printing the
                     * result */
                    for (msgend = IP_ReadMsgBuffer(hmsg); *msgend; )
                    {
                        DH_ParsePointString (&point, point.name, msgend,
                                             &msgend, NULL);
                        print_point (&point);
                    }
                    break;
                  case ST_DH_ECHO:
                    /* We never write a point, so ECHO is impossible in this
                     * app.  If we expected echoes, we could handle them in
                     * the same way as exceptions.  The message contents are
                     * identical for both. */
                    break;
                  default:
                    /* Async general ASCII message, sent by another task using
                     * the Cascade DataHub API.  One such task is the Cascade NameServer,
                     * nserve.  It will send taskstarted and
                     * taskdied messages when other tasks start and stop:
                     * (taskstarted <name> <queue> <domain> <node> <pid>)
                     * (taskdied <name> <queue> <domain> <node> <pid>)
                     */
                    break;
                }
                break;
              case IP_ERROR:
                /* Receive returned zero, which should never happen. */
                break;
              case IP_SYSTEM:
                /* A task death message that can only be received by a task
                 * that has set its QNX INFORMED bit.  You should avoid this,
                 * and use the taskdied and taskstarted messages in the
                 * IP_ASYNC section if possible. */
                break;
              case IP_SIGNAL:
                /* A signal caused the Receive to exit prematurely.  No message
                 * was received.  The value of sender is not defined. */
                break;
              case IP_NO_MSG:
                /* The queue reported a message waiting, but none was available.
                 * This happens if the application intentionally drains the queue,
                 * which is generally a bad idea. */
                break;
              case IP_SYNC:
                /* Synchronous general ASCII message, sent by another task using
                 * the Cascade DataHub API. */
                IP_FillMsg (hmsg, IP_NOP, ST_ERROR, "Unsupported", 12, NULL);
                IP_TaskReply (htask, sender, hmsg, NULL);
                break;
              case IP_RAW_MSG:
                /* Message sent using QNX Send command, not through the
                 * Cascade DataHub API.  This is also where a proxy would
                 * be delivered if we had set up a proxy within this task,
                 * for example, a periodic timer.  In that case, the proxy
                 * ID would be (pid_t)sender, and we would not Reply to it. */
                Reply ((pid_t)sender, "Unsupported", 12);
                break;
            }
        }
    }

Prev | Home | Next
Writing data to the Cascade DataHub | Up | A sample makefile definition

Copyright 1995-2002 by Cogent Real-Time Systems, Inc.