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	<unistd.h>
#include	<errno.h>

#include	<cogent.h>

#ifdef HAVE_SYS_TIME_H
#include	<sys/time.h>
#endif

const char* UT_USAGE =
	"Usage: %s [-d domain] [-r|-f|-i|-l|-s] [-S security] pointname pointvalue\n"
	;

const char* UT_HELP =
	"\n"
	"Help:\n"
	"    -d domain   Set the domain for the operation.\n"
	"    -r          Write as a real (floating point) number.\n"
	"    -i          Write as a short integer.\n"
	"    -l          Write as a long integer.\n"
	"    -s          Write as a character string.\n"
	"    -S          Set security level for write.\n"
	"\n"
	"Write a point to the Cascade datahub in the given domain.\n"
	"\n"
	"Notes:\n"
	"- Strings containing spaces and special characters must be escaped\n"
  	"    from the shell appropriately.\n"
	"\n"
	"- The type will be guessed if not specified, the order of guessing is:\n"
    "    1) long\n"
    "    2) float\n"
    "    3) string (the default)\n"
  	"    The guess is considered correct if the entire argument is converted.\n"
	"\n"
	"- A long can be given in standard C style, 0x5f (hex), 0647 (oct), etc.\n"
	;

int main (int argc, char** argv)
{
	IP_Msg			*hmsg;
	ST_STATUS		status;
	PT_stCPOINT		point;
	char			*ptname = NULL, *ptvalue = NULL, *domain=NULL, *myname;
	short			type = PT_TYPE_VOID;
	int				security=0;
	IP_Task			*htask;
	int				opt;

	/*
	 * Parse the command line
	 */
	while((opt = getopt(argc, argv, "hd:rilsS:")) != -1)
	{
		switch (opt)
		{
		  case 'h':
		  	UT_Help(argv[0], UT_USAGE, UT_HELP);
			exit(0);
		  case 'd':
			domain = optarg;
			if (strlen(domain) > 15)
				domain[15] = '\0';
			break;
		  case 'r':
			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 (optarg);
			break;
		  default:
			UT_Usage (argv[0], UT_USAGE, stderr);
			exit (1);
			break;
		}
	}

	/* the last two args better be the point and value */

	if (!argv[optind] || !argv[optind+1])
	{
		UT_Usage(argv[0], UT_USAGE, stderr);
		exit(1);
	}
	ptname	=	argv[optind];
	ptvalue	=	argv[optind+1];
	
	/*
	 * Initialize communication through the Cascade IPC library.  We do
	 * not want other tasks to be notified of the start and stop of
	 * this task, so we do not use IP_NserveInit.  The name server
	 * will never know about this task, so notifications will not be
	 * passed on.  Tasks that attempt to look up their clients in the
	 * name server will treat this task as non-existent.  For example,
	 * Cascade DataHub would not be able to send point exceptions to
	 * this task.
	 */

	if ((myname = strrchr(argv[0], '/')))
		myname++;
	else
		myname = argv[0];
	
	if (!(htask = IP_TaskCreateMe (IP_GetChannelID(), myname, domain,
								   NULL, 0)))
	{
		fprintf (stderr, "Could not initialize Cascade 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_MsgCreate (NULL, IP_MsgDefaultSize(), 0);
	
	/*
	 * 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.
	 */
#ifdef __QNX__
	{
		struct timespec	tp;
		
		clock_gettime (CLOCK_REALTIME, &tp);
		point.seconds = tp.tv_sec;
		point.nanoseconds = tp.tv_nsec;
	}
#else
	{
		struct timeval	tp;
		gettimeofday (&tp, NULL);
		point.seconds = tp.tv_sec;
		point.nanoseconds = tp.tv_usec * 1000;
	}		
#endif /* __QNX__ */

	/*
	 * Set the value of the point based on the type.
	 */
	switch (point.type)
	{
	  case PT_TYPE_INT32:
	  	point.value.i = strtol(ptvalue, 0, 0);
		break;

	  case PT_TYPE_REAL:
		point.value.r = strtod (ptvalue, NULL);
		break;

	  case PT_TYPE_STRING:
		point.value.s = ptvalue;
		break;

	  case PT_TYPE_VOID:
	  default:
	  {
		  /* try to autodetect type of point */
		  char* eos = 0;

		  /* it's a long if conversion goes to end of string */
		  eos = 0;
		  point.value.i = strtol(ptvalue, &eos, 0);
		  if(*eos == '\0')
		  {
			  point.type = PT_TYPE_INT32;
			  break;
		  }

		  /* it's a double if conversion goes to end of string */
		  eos = 0;
		  point.value.r = strtod(ptvalue, &eos);
		  if(*eos == '\0')
		  {
			  point.type = PT_TYPE_REAL;
			  break;
		  }

		  /* else it's a string */

		  point.type = PT_TYPE_STRING;
		  point.value.s = ptvalue;
		  break;
	  }
	}

	/*
	 * Write the point.  We need a IP_Msg 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);
}