A.2. Writing data to the Cascade DataHub

/*
 * Cascade DataHub point writer:  writept
 *
 * (C) Copyright Cogent Real-Time Systems Inc., 1997.  All rights reserved.
 *
 * This program writes a point to the Cascade DataHub.
 *
 * 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    <time.h>

#include    <cogent/cogdb.h>

#ifdef __USAGE
Copyright (C) Cogent Real-Time Systems Inc., 1996

%C [-d domain] [-r|-f|-i|-l|-s] [-S security] pointname pointvalue
    -d domain - set the domain for the operation
    -r|-f     - write as a floating point number
    -i        - write as a short integer
    -l        - write as a long integer
    -s        - write as a character string
    -S        - set security level for write

Write a point to the Cascade DataHub in the given
domain.  Strings containing spaces and special characters
must be escaped from the shell appropriately.

To write a negative number, use --value.
#endif

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

int main (int argc, char** argv)
{
    IP_hMSG            hmsg;
    ST_STATUS        status;
    PT_stCPOINT        point;
    char            *ptname = NULL, *ptvalue = NULL, *domain=NULL;
    short            type = PT_TYPE_INT32;
    int                i, security=0;
    IP_hTASK        htask;
    struct timespec    tp;

    /*
     * Parse the command line
     */
    for (i=1; i<argc; i++)
    {
        if (argv[i][0] == '-')
        {
            switch (argv[i][1])
            {
              case 'd':
                domain = (argv[i][2] ? &argv[i][2] : argv[++i]);
                if (strlen(domain) > 15)
                    domain[15] = '\0';
                break;
              case 'r':
              case 'f':
                type = PT_TYPE_REAL;
                break;
              case 'i':
              case 'l':
                type = PT_TYPE_INT32;
                break;
              case 's':
                type = PT_TYPE_STRING;
                break;
              case 'S':
                security = atoi (argv[i][2] ? &argv[i][2] : argv[++i]);
                break;
              case '-':
                ptvalue = &argv[i][1];
                break;
              default:
                Usage (argv);
                break;
            }
        }
        else
        {
            if (!ptname)
                ptname = argv[i];
            else
                ptvalue = argv[i];
        }
    }

    if (!ptname || !ptvalue)
    {
        printf ("Need a point and value\n");
        exit (1);
    }
    
    /*
     * Initialize communication with the Cascade NameServer.  We do
     * not want other tasks to be notified of the start and stop of this
     * task, so we make it NS_INVISIBLE.  The name server will know about
     * this task, it just will not tell anybody.  Also, since we do not
     * want to be receiving queue messages, do not name the queue.
     */
    if (!(htask = NS_Init (argv[0], NULL, domain, NS_INVISIBLE)))
    {
        fprintf (stderr, "Could not initialize Cascade DataHub IPC subsystem\n");
        exit (1);
    }

    /*
     * Set this task's security level.  This level must be greater than or
     * equal to the security level of the point in the datahub in order
     * for the write to succeed.  The datahub does not know whether this
     * task has the right to claim this security level.  That enforcement
     * is up to the programmer of the user task.
     */
    IP_TaskSetSecurity (htask, security);

    /*
     * Create a pre-allocated message structure for use with all
     * IPC calls.  This includes the DH_* functions.  The API could have
     * created its own internal message structure, but this would have
     * left us with no way to control its size or be efficient about
     * allocation.  This way we do a little more work, but have more
     * control of what is being allocated.
     */
    hmsg = IP_CreateMsg (0, 0, NULL, IP_MAX_MESSAGE, NULL);
    
    /*
     * Zero the point structure.  If we do not do this, the address
     * field could be non-zero, and then the API will take that to be a
     * cached datahub address.  That might cause a crash.
     */
    memset (&point, 0, sizeof(point));

    /*
     * 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 = ptname;
    point.type = type;
    point.conf = 100;

    /*
     * Set the time on the point.  If this is not set, then the datahub
     * will show a zero time.
     */
    clock_gettime (CLOCK_REALTIME, &tp);
    point.seconds = tp.tv_sec;
    point.nanoseconds = tp.tv_nsec;

    /*
     * Set the value of the point based on the type.
     */
    switch (point.type)
    {
      case PT_TYPE_INT32:
        point.value.i = atoi (ptvalue);
        break;
      case PT_TYPE_REAL:
        point.value.r = strtod (ptvalue, NULL);
        break;
      case PT_TYPE_STRING:
        point.value.s = ptvalue;
        break;
    }

    /*
     * Write the point.  We need a IP_hMSG structure and a IP_hTASK in
     * to provide buffer space and sender identification respectively.
     */
    if ((status = DH_WritePoint (htask, &point, hmsg, NULL)) != ST_OK)
        printf ("Write point failed: %s\n", ST_StatusName (status));
    
    return (0);
}

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