/*
* 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;
}
}
}