This commit was manufactured by cvs2svn to create branch 'B3.14'.
This commit is contained in:
34
src/catools/Makefile
Normal file
34
src/catools/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer
|
||||
# Synchrotronstrahlung.
|
||||
# EPICS BASE Versions 3.13.7
|
||||
# and higher are distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
SHARED_LIBRARIES = NO
|
||||
#STATIC_BUILD=YES
|
||||
|
||||
LIBRARY_HOST += catools
|
||||
|
||||
catools_SRCS += tool_lib.c
|
||||
catools_LIBS += ca Com
|
||||
|
||||
#Remove LIBNAME from the library names to be installed (make it a local library)
|
||||
INSTALL_LIBS= $(addprefix $(INSTALL_LIB)/,$(filterout $(TESTLIBNAME),$(LIBNAME)))
|
||||
|
||||
PROD_HOST += caget
|
||||
|
||||
caget_SRCS += caget.c
|
||||
caget_LIBS += catools ca Com
|
||||
|
||||
catools_DIR=.
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
471
src/catools/caget.c
Normal file
471
src/catools/caget.c
Normal file
@@ -0,0 +1,471 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer
|
||||
* Synchrotronstrahlung.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Author: Ralph Lange (BESSY)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#include <alarm.h>
|
||||
#include <tsDefs.h>
|
||||
#include <cadef.h>
|
||||
|
||||
#include "tool_lib.h"
|
||||
|
||||
#define VALID_DOUBLE_DIGITS 18 /* Max usable precision for a double */
|
||||
#define PEND_EVENT_SLICES 5 /* No. of pend_event slices for callback requests */
|
||||
|
||||
/* Different output formats */
|
||||
typedef enum { plain, terse, all, specifiedDbr } OutputT;
|
||||
|
||||
/* Different request types */
|
||||
typedef enum { get, callback } RequestT;
|
||||
|
||||
static int nConn = 0; /* Number of connected PVs */
|
||||
static int nRead = 0; /* Number of channels that were read */
|
||||
|
||||
|
||||
void usage (void)
|
||||
{
|
||||
fprintf (stderr, "\nUsage: caget [options] <PV name> ...\n\n"
|
||||
" -h: Help: Print this message\n"
|
||||
"Channel Access options:\n"
|
||||
" -w <sec>: Wait time, specifies longer CA timeout, default is %f second\n"
|
||||
" -c: Asynchronous get (use ca_get_callback instead of ca_get)\n"
|
||||
"Format options:\n"
|
||||
" Default output format is \"name value\"\n"
|
||||
" -t: Terse mode - print only value, without name\n"
|
||||
" -a: Wide mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n"
|
||||
" -n: Print DBF_ENUM values as number (default are enum string values)\n"
|
||||
" -d <type>: Request specific dbr type; use string (DBR_ prefix may be omitted)\n"
|
||||
" or number of one of the following types:\n"
|
||||
" DBR_STRING 0 DBR_STS_FLOAT 9 DBR_TIME_LONG 19 DBR_CTRL_SHORT 29\n"
|
||||
" DBR_INT 1 DBR_STS_ENUM 10 DBR_TIME_DOUBLE 20 DBR_CTRL_INT 29\n"
|
||||
" DBR_SHORT 1 DBR_STS_CHAR 11 DBR_GR_STRING 21 DBR_CTRL_FLOAT 30\n"
|
||||
" DBR_FLOAT 2 DBR_STS_LONG 12 DBR_GR_SHORT 22 DBR_CTRL_ENUM 31\n"
|
||||
" DBR_ENUM 3 DBR_STS_DOUBLE 13 DBR_GR_INT 22 DBR_CTRL_CHAR 32\n"
|
||||
" DBR_CHAR 4 DBR_TIME_STRING 14 DBR_GR_FLOAT 23 DBR_CTRL_LONG 33\n"
|
||||
" DBR_LONG 5 DBR_TIME_INT 15 DBR_GR_ENUM 24 DBR_CTRL_DOUBLE 34\n"
|
||||
" DBR_DOUBLE 6 DBR_TIME_SHORT 15 DBR_GR_CHAR 25 DBR_STSACK_STRING 37\n"
|
||||
" DBR_STS_STRING 7 DBR_TIME_FLOAT 16 DBR_GR_LONG 26 DBR_CLASS_NAME 38\n"
|
||||
" DBR_STS_SHORT 8 DBR_TIME_ENUM 17 DBR_GR_DOUBLE 27\n"
|
||||
" DBR_STS_INT 8 DBR_TIME_CHAR 18 DBR_CTRL_STRING 28\n"
|
||||
"Arrays: Value format: print number of requested values, then list of values\n"
|
||||
" Default: Print all values\n"
|
||||
" -# <count>: Print first <count> elements of an array\n"
|
||||
"Floating point type format:\n"
|
||||
" Default: Use g format\n"
|
||||
" -f <nr>: Use f format, with <nr> digits after the decimal point\n"
|
||||
" -e <nr>: Use e format, with <nr> digits after the decimal point\n"
|
||||
"Integer number format:\n"
|
||||
" Default: Print as decimal number\n"
|
||||
" -0x: Print as hex number\n"
|
||||
" -0o: Print as octal number\n"
|
||||
" -0b: Print as binary number\n"
|
||||
"\nExample: caget -a -f8 my_channel another_channel\n"
|
||||
" (uses wide output format, doubles are printed as %%f with precision of 8)\n\n"
|
||||
, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*+**************************************************************************
|
||||
*
|
||||
* Function: event_handler
|
||||
*
|
||||
* Description: CA event_handler for request type callback
|
||||
* Allocates the dbr structure and copies the data
|
||||
*
|
||||
* Arg(s) In: args - event handler args (see CA manual)
|
||||
*
|
||||
**************************************************************************-*/
|
||||
|
||||
void event_handler (evargs args)
|
||||
{
|
||||
pv* pv = args.usr;
|
||||
|
||||
pv->status = args.status;
|
||||
if (args.status == ECA_NORMAL)
|
||||
{
|
||||
pv->dbrType = args.type;
|
||||
pv->value = calloc(1, dbr_size_n(args.type, args.count));
|
||||
memcpy(pv->value, args.dbr, dbr_size_n(args.type, args.count));
|
||||
nRead++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*+**************************************************************************
|
||||
*
|
||||
* Function: caget
|
||||
*
|
||||
* Description: Issue read requests, wait for incoming data
|
||||
* and print the data according to the selected format
|
||||
*
|
||||
* Arg(s) In: pvs - Pointer to an array of pv structures
|
||||
* nPvs - Number of elements in the pvs array
|
||||
* request - Request type
|
||||
* format - Output format
|
||||
* dbrType - Requested dbr type
|
||||
* reqElems - Requested number of (array) elements
|
||||
*
|
||||
* Return(s): Error code: 0 = OK, 1 = Error
|
||||
*
|
||||
**************************************************************************-*/
|
||||
|
||||
/* caget - Get PV values */
|
||||
int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
|
||||
chtype dbrType, unsigned long reqElems)
|
||||
{
|
||||
int i, n, result;
|
||||
|
||||
for (n = 0; n < nPvs; n++) {
|
||||
|
||||
/* Set up pvs structure */
|
||||
/* -------------------- */
|
||||
|
||||
/* Get natural type and array count */
|
||||
pvs[n].nElems = ca_element_count(pvs[n].chid);
|
||||
pvs[n].dbfType = ca_field_type(pvs[n].chid);
|
||||
|
||||
/* Set up value structures */
|
||||
if (format != specifiedDbr)
|
||||
{
|
||||
dbrType = dbf_type_to_DBR_TIME(pvs[n].dbfType); /* Use native type */
|
||||
if (dbr_type_is_ENUM(dbrType)) /* Enums honour -n option */
|
||||
{
|
||||
if (charAsNr) dbrType = DBR_TIME_INT;
|
||||
else dbrType = DBR_TIME_STRING;
|
||||
}
|
||||
}
|
||||
/* Adjust array count */
|
||||
if (reqElems == 0 || pvs[n].nElems < reqElems)
|
||||
reqElems = pvs[n].nElems;
|
||||
/* Remember dbrType and reqElems */
|
||||
pvs[n].dbrType = dbrType;
|
||||
pvs[n].reqElems = reqElems;
|
||||
|
||||
/* Issue CA request */
|
||||
/* ---------------- */
|
||||
|
||||
if (ca_state(pvs[n].chid) == cs_conn)
|
||||
{
|
||||
nConn++;
|
||||
if (request == callback)
|
||||
{ /* Event handler will allocate value */
|
||||
result = ca_array_get_callback(dbrType,
|
||||
reqElems,
|
||||
pvs[n].chid,
|
||||
event_handler,
|
||||
(void*)&pvs[n]);
|
||||
} else {
|
||||
/* Allocate value structure */
|
||||
pvs[n].value = calloc(1, dbr_size_n(dbrType, reqElems));
|
||||
result = ca_array_get(dbrType,
|
||||
reqElems,
|
||||
pvs[n].chid,
|
||||
pvs[n].value);
|
||||
}
|
||||
pvs[n].status = result;
|
||||
} else {
|
||||
pvs[n].status = ECA_DISCONN;
|
||||
}
|
||||
}
|
||||
if (!nConn) return 1; /* No connection? We're done. */
|
||||
|
||||
/* Wait for completion */
|
||||
/* ------------------- */
|
||||
|
||||
result = ca_pend_io(timeout);
|
||||
if (result == ECA_TIMEOUT)
|
||||
fprintf(stderr, "Read operation timed out: some PV data was not read.\n");
|
||||
|
||||
if (request == callback) /* Also wait for callbacks */
|
||||
{
|
||||
double slice = timeout / PEND_EVENT_SLICES;
|
||||
for (n = 0; n < PEND_EVENT_SLICES; n++)
|
||||
{
|
||||
ca_pend_event(slice);
|
||||
if (nRead >= nConn) break;
|
||||
}
|
||||
if (nRead < nConn)
|
||||
fprintf(stderr, "Read operation timed out: some PV data was not read.\n");
|
||||
}
|
||||
|
||||
/* Print the data */
|
||||
/* -------------- */
|
||||
|
||||
for (n = 0; n < nPvs; n++) {
|
||||
reqElems = pvs[n].reqElems;
|
||||
|
||||
switch (format) {
|
||||
case plain: /* Emulate old caget behaviour */
|
||||
if (reqElems <= 1) printf("%-30s ", pvs[n].name);
|
||||
else printf("%s", pvs[n].name);
|
||||
case terse:
|
||||
if (pvs[n].status == ECA_DISCONN)
|
||||
printf("*** not connected\n");
|
||||
else if (pvs[n].status == ECA_NORDACCESS)
|
||||
printf("*** no read access\n");
|
||||
else if (pvs[n].status != ECA_NORMAL)
|
||||
printf("*** CA error %s\n", ca_message(pvs[n].status));
|
||||
else if (pvs[n].value == 0)
|
||||
printf("*** no data available (timeout)\n");
|
||||
else
|
||||
{
|
||||
if (reqElems > 1) printf(" %lu ", reqElems);
|
||||
for (i=0; i<reqElems; ++i)
|
||||
printf("%s ", val2str(pvs[n].value, pvs[n].dbrType, i));
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
case all:
|
||||
print_time_val_sts(&pvs[n], reqElems);
|
||||
break;
|
||||
case specifiedDbr:
|
||||
printf("%s\n", pvs[n].name);
|
||||
if (pvs[n].status == ECA_DISCONN)
|
||||
printf(" *** not connected\n");
|
||||
else if (pvs[n].status == ECA_NORDACCESS)
|
||||
printf(" *** no read access\n");
|
||||
else if (pvs[n].status != ECA_NORMAL)
|
||||
printf(" *** CA error %s\n", ca_message(pvs[n].status));
|
||||
else
|
||||
{
|
||||
printf(" Data type: %s (native: %s)\n",
|
||||
dbr_type_to_text(pvs[n].dbrType),
|
||||
dbf_type_to_text(pvs[n].dbfType));
|
||||
if (pvs[n].dbrType == DBR_CLASS_NAME)
|
||||
printf(" Class Name: %s\n",
|
||||
*((dbr_string_t*)dbr_value_ptr(pvs[n].value,
|
||||
pvs[n].dbrType)));
|
||||
else {
|
||||
printf(" Element count: %lu\n"
|
||||
" Value: ",
|
||||
reqElems);
|
||||
for (i=0; i<reqElems; ++i) /* Print value(s) */
|
||||
printf("%s ", val2str(pvs[n].value, pvs[n].dbrType, i));
|
||||
printf("\n");
|
||||
if (pvs[n].dbrType > DBR_DOUBLE) /* Extended type extra info */
|
||||
printf("%s\n", dbr2str(pvs[n].value, pvs[n].dbrType));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*+**************************************************************************
|
||||
*
|
||||
* Function: main
|
||||
*
|
||||
* Description: caget main()
|
||||
* Evaluate command line options, set up CA, connect the
|
||||
* channels, collect and print the data as requested
|
||||
*
|
||||
* Arg(s) In: [options] <pv-name> ...
|
||||
*
|
||||
* Arg(s) Out: none
|
||||
*
|
||||
* Return(s): Standard return code (0=success, 1=error)
|
||||
*
|
||||
**************************************************************************-*/
|
||||
|
||||
void complainIfNotPlainAndSet (OutputT *current, const OutputT requested)
|
||||
{
|
||||
if (*current != plain)
|
||||
fprintf(stderr,
|
||||
"Options t,d,a are mutually exclusive. "
|
||||
"('caget -h' for help.)\n");
|
||||
*current = requested;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int n = 0;
|
||||
int result; /* CA result */
|
||||
OutputT format = plain; /* User specified format */
|
||||
RequestT request = get; /* User specified request type */
|
||||
|
||||
int count = 0; /* 0 = not specified by -# option */
|
||||
int opt; /* getopt() current option */
|
||||
int type = -1; /* getopt() data type argument */
|
||||
int digits = 0; /* getopt() no. of float digits */
|
||||
|
||||
int nPvs; /* Number of PVs */
|
||||
pv* pvs = 0; /* Array of PV structures */
|
||||
|
||||
setvbuf(stdout,NULL,_IOLBF,0); /* Set stdout to line buffering */
|
||||
|
||||
while ((opt = getopt(argc, argv, ":taicnhe:f:#:d:0:w:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h': /* Print usage */
|
||||
usage();
|
||||
return 0;
|
||||
case 't': /* Terse output mode */
|
||||
complainIfNotPlainAndSet(&format, terse);
|
||||
break;
|
||||
case 'a': /* Wide output mode */
|
||||
complainIfNotPlainAndSet(&format, all);
|
||||
break;
|
||||
case 'c': /* Callback mode */
|
||||
request = callback;
|
||||
break;
|
||||
case 'd': /* Data type specification */
|
||||
complainIfNotPlainAndSet(&format, specifiedDbr);
|
||||
/* Argument (type) may be text or number */
|
||||
if (sscanf(optarg, "%d", &type) != 1)
|
||||
{
|
||||
dbr_text_to_type(optarg, type);
|
||||
if (type == -1) /* Invalid? Try prefix DBR_ */
|
||||
{
|
||||
char str[30] = "DBR_";
|
||||
strncat(str, optarg, 25);
|
||||
dbr_text_to_type(str, type);
|
||||
}
|
||||
}
|
||||
if (type < DBR_STRING || type > DBR_CLASS_NAME
|
||||
|| type == DBR_PUT_ACKT || type == DBR_PUT_ACKS)
|
||||
{
|
||||
fprintf(stderr, "Requested dbr type out of range "
|
||||
"or invalid - ignored. ('caget -h' for help.)\n");
|
||||
format = plain;
|
||||
}
|
||||
break;
|
||||
case 'n': /* Print ENUM as index numbers */
|
||||
charAsNr=1;
|
||||
break;
|
||||
case 'w': /* Set CA timeout value */
|
||||
if(sscanf(optarg,"%lf", &timeout) != 1)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not a valid timeout value "
|
||||
"- ignored. ('caget -h' for help.)\n", optarg);
|
||||
timeout = DEFAULT_TIMEOUT;
|
||||
}
|
||||
break;
|
||||
case '#': /* Array count */
|
||||
if (sscanf(optarg,"%d", &count) != 1)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not a valid array element count "
|
||||
"- ignored. ('caget -h' for help.)\n", optarg);
|
||||
count = 0;
|
||||
}
|
||||
break;
|
||||
case 'e': /* Select %e format, using <arg> digits */
|
||||
if (sscanf(optarg, "%d", &digits) != 1)
|
||||
fprintf(stderr,
|
||||
"Invalid precision argument '%s' "
|
||||
"for option '-e' - ignored.\n", optarg);
|
||||
else
|
||||
{
|
||||
if (digits>=0 && digits<=VALID_DOUBLE_DIGITS)
|
||||
sprintf(dblFormatStr, "%%-.%de", digits);
|
||||
else
|
||||
fprintf(stderr, "Precision %d for option '-e' "
|
||||
"out of range - ignored.\n", digits);
|
||||
}
|
||||
break;
|
||||
case 'f': /* Select %f format, using <arg> digits */
|
||||
if (sscanf(optarg, "%d", &digits) != 1)
|
||||
fprintf(stderr, "Invalid precision argument '%s' "
|
||||
"for option '-f' - ignored.\n", optarg);
|
||||
else
|
||||
{
|
||||
if (digits>=0 && digits<=VALID_DOUBLE_DIGITS)
|
||||
sprintf(dblFormatStr, "%%-.%df", digits);
|
||||
else
|
||||
fprintf(stderr, "Precision %d for option '-f' "
|
||||
"out of range - ignored.\n", digits);
|
||||
}
|
||||
break;
|
||||
case '0': /* Select integer format */
|
||||
type = DBR_LONG;
|
||||
switch ((char) *optarg) {
|
||||
case 'x': outType = hex; break; /* 0x print Hex */
|
||||
case 'b': outType = bin; break; /* 0b print Binary */
|
||||
case 'o': outType = oct; break; /* 0o print Octal */
|
||||
default :
|
||||
fprintf(stderr, "Invalid argument '%s' "
|
||||
"for option '-0' - ignored.\n", optarg);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr,
|
||||
"Unrecognized option: '-%c'. ('caget -h' for help.)\n",
|
||||
optopt);
|
||||
return 1;
|
||||
case ':':
|
||||
fprintf(stderr,
|
||||
"Option '-%c' requires an argument. ('caget -h' for help.)\n",
|
||||
optopt);
|
||||
return 1;
|
||||
default :
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
nPvs = argc - optind; /* Remaining arg list are PV names */
|
||||
|
||||
if (nPvs < 1)
|
||||
{
|
||||
fprintf(stderr, "No pv name specified. ('caget -h' for help.)\n");
|
||||
return 1;
|
||||
}
|
||||
/* Start up Channel Access */
|
||||
|
||||
result = ca_context_create(ca_disable_preemptive_callback);
|
||||
if (result != ECA_NORMAL) {
|
||||
fprintf(stderr, "CA error %s occurred while trying "
|
||||
"to start channel access '%s'.\n", ca_message(result), pvs[n].name);
|
||||
return 1;
|
||||
}
|
||||
/* Allocate PV structure array */
|
||||
|
||||
pvs = calloc (nPvs, sizeof(pv));
|
||||
if (!pvs)
|
||||
{
|
||||
fprintf(stderr, "Memory allocation for channel structures failed.\n");
|
||||
return 1;
|
||||
}
|
||||
/* Connect channels */
|
||||
|
||||
for (n = 0; optind < argc; n++, optind++)
|
||||
pvs[n].name = argv[optind]; /* Copy PV names from command line */
|
||||
|
||||
connect_pvs(pvs, nPvs);
|
||||
|
||||
/* Read and print data */
|
||||
|
||||
result = caget(pvs, nPvs, request, format, type, count);
|
||||
|
||||
/* Shut down Channel Access */
|
||||
ca_context_destroy();
|
||||
|
||||
return result;
|
||||
}
|
||||
517
src/catools/tool_lib.c
Normal file
517
src/catools/tool_lib.c
Normal file
@@ -0,0 +1,517 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer
|
||||
* Synchrotronstrahlung.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Author: Ralph Lange (BESSY)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#define epicsAlarmGLOBAL
|
||||
#include <alarm.h>
|
||||
#undef epicsAlarmGLOBAL
|
||||
#include <tsDefs.h>
|
||||
#include <cadef.h>
|
||||
|
||||
#include "tool_lib.h"
|
||||
|
||||
/* Time stamps for program start, previous value (used for differential
|
||||
* times with monitors) */
|
||||
static TS_STAMP tsStart, tsPrev;
|
||||
|
||||
static int tsInit = 0; /* Flag: Timestamps init'd */
|
||||
static int firstStampPrinted = 0; /* Flag: First timestamp already printed */
|
||||
|
||||
TimeT tsType = absTime; /* Timestamp type flag (-q or -Q option) */
|
||||
IntFormatT outType = dec; /* For -0.. output format option */
|
||||
|
||||
char dblFormatStr[30] = "%g"; /* Format string to print doubles (see -e -f option) */
|
||||
|
||||
int charAsNr = 0; /* used for -n option - get DBF_CHAR as number */
|
||||
double timeout = 1.0; /* wait time default (see -w option) */
|
||||
|
||||
|
||||
|
||||
void sprint_long (char *ret, long val)
|
||||
{
|
||||
long i, bit, skip=-1L; /* used only for printing bits */
|
||||
switch (outType) {
|
||||
case hex: sprintf(ret, "0x%lX", val); break;
|
||||
case oct: sprintf(ret, "0o%lo", val); break;
|
||||
case bin:
|
||||
for (i=31; i>=0 ; i--)
|
||||
{
|
||||
bit = (val>>i) & 0x1L;
|
||||
if (skip<0 && bit)
|
||||
{
|
||||
skip = 31 - i; /* skip leading 0's */
|
||||
ret[i+1] = '\0';
|
||||
}
|
||||
if (skip >= 0)
|
||||
{
|
||||
ret[31-i-skip] = (bit) ? '1' : '0';
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: sprintf(ret, "%ld", val); /* decimal */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*+**************************************************************************
|
||||
*
|
||||
* Function: val2str
|
||||
*
|
||||
* Description: Print (convert) value to a string
|
||||
*
|
||||
* Arg(s) In: v - Pointer to dbr_... structure
|
||||
* type - Numeric dbr type
|
||||
* index - Index of element to print (for arrays)
|
||||
*
|
||||
* Return(s): Pointer to static output string
|
||||
*
|
||||
**************************************************************************-*/
|
||||
|
||||
char *val2str (const void *v, unsigned type, int index)
|
||||
{
|
||||
static char str[500];
|
||||
char ch;
|
||||
void *val_ptr;
|
||||
unsigned base_type;
|
||||
|
||||
if (!dbr_type_is_valid(type)) {
|
||||
strcpy (str, "*** invalid type");
|
||||
return str;
|
||||
}
|
||||
|
||||
base_type = type % (LAST_TYPE+1);
|
||||
|
||||
if (type == DBR_STSACK_STRING || type == DBR_CLASS_NAME)
|
||||
base_type = DBR_STRING;
|
||||
|
||||
val_ptr = dbr_value_ptr(v, type);
|
||||
|
||||
switch (base_type) {
|
||||
case DBR_STRING:
|
||||
sprintf(str, "%s", ((dbr_string_t*)val_ptr)[index]);
|
||||
break;
|
||||
case DBR_FLOAT:
|
||||
sprintf(str, dblFormatStr, ((dbr_float_t*) val_ptr)[index]);
|
||||
break;
|
||||
case DBR_DOUBLE:
|
||||
sprintf(str, dblFormatStr, ((dbr_double_t*)val_ptr)[index]);
|
||||
break;
|
||||
case DBR_CHAR:
|
||||
ch = ((dbr_char_t*) val_ptr)[index];
|
||||
if(ch > 31 )
|
||||
sprintf(str, "%d \'%c\'",ch,ch);
|
||||
else
|
||||
sprintf(str, "%d",ch);
|
||||
break;
|
||||
case DBR_INT:
|
||||
sprint_long(str, ((dbr_int_t*) val_ptr)[index]);
|
||||
break;
|
||||
case DBR_LONG:
|
||||
sprint_long(str, ((dbr_long_t*) val_ptr)[index]);
|
||||
break;
|
||||
case DBR_ENUM:
|
||||
{
|
||||
dbr_enum_t *val = (dbr_enum_t *)val_ptr;
|
||||
if (dbr_type_is_GR(type))
|
||||
sprintf(str,"%s (%d)",
|
||||
((struct dbr_gr_enum *)v)->strs[val[index]],val[index]);
|
||||
else if (dbr_type_is_CTRL(type))
|
||||
sprintf(str,"%s (%d)",
|
||||
((struct dbr_ctrl_enum *)v)->strs[val[index]],val[index]);
|
||||
else
|
||||
sprintf(str, "%d", val[index]);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*+**************************************************************************
|
||||
*
|
||||
* Function: dbr2str
|
||||
*
|
||||
* Description: Print (convert) additional information contained in dbr_...
|
||||
*
|
||||
* Arg(s) In: value - Pointer to dbr_... structure
|
||||
* type - Numeric dbr type
|
||||
*
|
||||
* Return(s): Pointer to static output string
|
||||
*
|
||||
**************************************************************************-*/
|
||||
|
||||
/* Definitions for sprintf format strings and matching argument lists */
|
||||
|
||||
#define FMT_TIME \
|
||||
" Timestamp: %s"
|
||||
|
||||
#define ARGS_TIME(T) \
|
||||
tsStampToText( &(((struct T *)value)->stamp), \
|
||||
TS_TEXT_MMDDYY, \
|
||||
timeText)
|
||||
|
||||
#define FMT_STS \
|
||||
" Status: %s\n" \
|
||||
" Severity: %s"
|
||||
|
||||
#define ARGS_STS(T) \
|
||||
stat_to_str(((struct T *)value)->status), \
|
||||
sevr_to_str(((struct T *)value)->severity)
|
||||
|
||||
#define FMT_ACK \
|
||||
" Ack transient?: %s\n" \
|
||||
" Ack severity: %s"
|
||||
|
||||
#define ARGS_ACK(T) \
|
||||
((struct T *)value)->ackt ? "YES" : "NO", \
|
||||
sevr_to_str(((struct T *)value)->acks)
|
||||
|
||||
#define FMT_UNITS \
|
||||
" Units: %s"
|
||||
|
||||
#define ARGS_UNITS(T) \
|
||||
((struct T *)value)->units
|
||||
|
||||
#define FMT_PREC \
|
||||
" Precision: %d"
|
||||
|
||||
#define ARGS_PREC(T) \
|
||||
((struct T *)value)->precision
|
||||
|
||||
#define FMT_GR(FMT) \
|
||||
" Lo disp limit: " #FMT "\n" \
|
||||
" Hi disp limit: " #FMT "\n" \
|
||||
" Lo alarm limit: " #FMT "\n" \
|
||||
" Lo warn limit: " #FMT "\n" \
|
||||
" Hi warn limit: " #FMT "\n" \
|
||||
" Hi alarm limit: " #FMT
|
||||
|
||||
#define ARGS_GR(T,F) \
|
||||
(F)((struct T *)value)->lower_disp_limit, \
|
||||
(F)((struct T *)value)->upper_disp_limit, \
|
||||
(F)((struct T *)value)->lower_alarm_limit, \
|
||||
(F)((struct T *)value)->lower_warning_limit, \
|
||||
(F)((struct T *)value)->upper_warning_limit, \
|
||||
(F)((struct T *)value)->upper_alarm_limit
|
||||
|
||||
#define FMT_CTRL(FMT) \
|
||||
" Lo ctrl limit: " #FMT "\n" \
|
||||
" Hi ctrl limit: " #FMT
|
||||
|
||||
#define ARGS_CTRL(T,F) \
|
||||
(F)((struct T *)value)->lower_ctrl_limit, \
|
||||
(F)((struct T *)value)->upper_ctrl_limit
|
||||
|
||||
|
||||
/* Definitions for the actual sprintf calls */
|
||||
|
||||
#define PRN_DBR_STS(T) \
|
||||
sprintf(str, \
|
||||
FMT_STS, \
|
||||
ARGS_STS(T))
|
||||
|
||||
#define PRN_DBR_TIME(T) \
|
||||
sprintf(str, \
|
||||
FMT_TIME "\n" FMT_STS, \
|
||||
ARGS_TIME(T), ARGS_STS(T))
|
||||
|
||||
#define PRN_DBR_GR(T,F,FMT) \
|
||||
sprintf(str, \
|
||||
FMT_STS "\n" FMT_UNITS "\n" FMT_GR(FMT), \
|
||||
ARGS_STS(T), ARGS_UNITS(T), ARGS_GR(T,F))
|
||||
|
||||
#define PRN_DBR_GR_PREC(T,F,FMT) \
|
||||
sprintf(str, \
|
||||
FMT_STS "\n" FMT_UNITS "\n" FMT_PREC "\n" FMT_GR(FMT), \
|
||||
ARGS_STS(T), ARGS_UNITS(T), ARGS_PREC(T), ARGS_GR(T,F))
|
||||
|
||||
#define PRN_DBR_CTRL(T,F,FMT) \
|
||||
sprintf(str, \
|
||||
FMT_STS "\n" FMT_UNITS "\n" FMT_GR(FMT) "\n" FMT_CTRL(FMT), \
|
||||
ARGS_STS(T), ARGS_UNITS(T), ARGS_GR(T,F), ARGS_CTRL(T,F))
|
||||
|
||||
#define PRN_DBR_CTRL_PREC(T,F,FMT) \
|
||||
sprintf(str, \
|
||||
FMT_STS "\n" FMT_UNITS "\n" FMT_PREC "\n" FMT_GR(FMT) "\n" FMT_CTRL(FMT), \
|
||||
ARGS_STS(T), ARGS_UNITS(T), ARGS_PREC(T), ARGS_GR(T,F), ARGS_CTRL(T,F))
|
||||
|
||||
#define PRN_DBR_STSACK(T) \
|
||||
sprintf(str, \
|
||||
FMT_STS "\n" FMT_ACK, \
|
||||
ARGS_STS(T), ARGS_ACK(T))
|
||||
|
||||
#define PRN_DBR_X_ENUM(T) \
|
||||
n = ((struct T *)value)->no_str; \
|
||||
PRN_DBR_STS(T); \
|
||||
sprintf(str+strlen(str), \
|
||||
"\n Enums: (%2d)", n); \
|
||||
for (i=0; i<n; i++) \
|
||||
sprintf(str+strlen(str), \
|
||||
"\n [%2d] %s", i, \
|
||||
((struct T *)value)->strs[i]);
|
||||
|
||||
|
||||
/* Make a good guess how long the dbr_... stuff might get as worst case */
|
||||
#define DBR_PRINT_BUFFER_SIZE \
|
||||
50 /* timestamp */ \
|
||||
+ 2 * 30 /* status / Severity */ \
|
||||
+ 2 * 30 /* acks / ackt */ \
|
||||
+ 20 + MAX_UNITS_SIZE /* units */ \
|
||||
+ 30 /* precision */ \
|
||||
+ 6 * 45 /* graphic limits */ \
|
||||
+ 2 * 45 /* control limits */ \
|
||||
+ 30 + (MAX_ENUM_STATES * (20 + MAX_ENUM_STRING_SIZE)) /* enums */ \
|
||||
+ 50 /* just to be sure */
|
||||
|
||||
char *dbr2str (const void *value, unsigned type)
|
||||
{
|
||||
static char str[DBR_PRINT_BUFFER_SIZE];
|
||||
char timeText[28];
|
||||
int n, i;
|
||||
|
||||
switch (type) {
|
||||
case DBR_STRING: /* no additional information for basic data types */
|
||||
case DBR_INT:
|
||||
case DBR_FLOAT:
|
||||
case DBR_ENUM:
|
||||
case DBR_CHAR:
|
||||
case DBR_LONG:
|
||||
case DBR_DOUBLE: break;
|
||||
|
||||
case DBR_CTRL_STRING: /* see db_access.h: not implemented */
|
||||
case DBR_GR_STRING: /* see db_access.h: not implemented */
|
||||
case DBR_STS_STRING: PRN_DBR_STS(dbr_sts_string); break;
|
||||
case DBR_STS_SHORT: PRN_DBR_STS(dbr_sts_short); break;
|
||||
case DBR_STS_FLOAT: PRN_DBR_STS(dbr_sts_float); break;
|
||||
case DBR_STS_ENUM: PRN_DBR_STS(dbr_sts_enum); break;
|
||||
case DBR_STS_CHAR: PRN_DBR_STS(dbr_sts_char); break;
|
||||
case DBR_STS_LONG: PRN_DBR_STS(dbr_sts_long); break;
|
||||
case DBR_STS_DOUBLE: PRN_DBR_STS(dbr_sts_double); break;
|
||||
|
||||
case DBR_TIME_STRING: PRN_DBR_TIME(dbr_time_string); break;
|
||||
case DBR_TIME_SHORT: PRN_DBR_TIME(dbr_time_short); break;
|
||||
case DBR_TIME_FLOAT: PRN_DBR_TIME(dbr_time_float); break;
|
||||
case DBR_TIME_ENUM: PRN_DBR_TIME(dbr_time_enum); break;
|
||||
case DBR_TIME_CHAR: PRN_DBR_TIME(dbr_time_char); break;
|
||||
case DBR_TIME_LONG: PRN_DBR_TIME(dbr_time_long); break;
|
||||
case DBR_TIME_DOUBLE: PRN_DBR_TIME(dbr_time_double); break;
|
||||
|
||||
case DBR_GR_CHAR:
|
||||
PRN_DBR_GR(dbr_gr_char, char, %8d); break;
|
||||
case DBR_GR_INT:
|
||||
PRN_DBR_GR(dbr_gr_int, int, %8d); break;
|
||||
case DBR_GR_LONG:
|
||||
PRN_DBR_GR(dbr_gr_long, long int, %8ld); break;
|
||||
case DBR_GR_FLOAT:
|
||||
PRN_DBR_GR_PREC(dbr_gr_float, float, %g); break;
|
||||
case DBR_GR_DOUBLE:
|
||||
PRN_DBR_GR_PREC(dbr_gr_double, double, %g); break;
|
||||
case DBR_GR_ENUM:
|
||||
PRN_DBR_X_ENUM(dbr_gr_enum); break;
|
||||
case DBR_CTRL_CHAR:
|
||||
PRN_DBR_CTRL(dbr_ctrl_char, char, %8d); break;
|
||||
case DBR_CTRL_INT:
|
||||
PRN_DBR_CTRL(dbr_ctrl_int, int, %8d); break;
|
||||
case DBR_CTRL_LONG:
|
||||
PRN_DBR_CTRL(dbr_ctrl_long, long int, %8ld); break;
|
||||
case DBR_CTRL_FLOAT:
|
||||
PRN_DBR_CTRL_PREC(dbr_ctrl_float, float, %g); break;
|
||||
case DBR_CTRL_DOUBLE:
|
||||
PRN_DBR_CTRL_PREC(dbr_ctrl_double, double, %g); break;
|
||||
case DBR_CTRL_ENUM:
|
||||
PRN_DBR_X_ENUM(dbr_ctrl_enum); break;
|
||||
case DBR_STSACK_STRING:
|
||||
PRN_DBR_STSACK(dbr_stsack_string); break;
|
||||
default : strcpy (str, "can't print data type");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/* tsDef.h: TsDiffAsDouble macro defined in 3.13 is no longer available in 3.14 */
|
||||
double tsDiffDbl(TS_STAMP *ts1, TS_STAMP * ts2)
|
||||
{
|
||||
double tdiff;
|
||||
tdiff = ( ((double) ts1->nsec) - ((double)ts2->nsec)) / 1000000000.;
|
||||
tdiff += ((double) ts1->secPastEpoch) - ((double)ts2->secPastEpoch);
|
||||
return tdiff;
|
||||
}
|
||||
|
||||
|
||||
/*+**************************************************************************
|
||||
*
|
||||
* Function: print_time_val_sts
|
||||
*
|
||||
* Description: Print (to stdout) one wide output line
|
||||
* (name, timestamp, value, status, severity)
|
||||
*
|
||||
* Arg(s) In: pv - Pointer to pv structure
|
||||
* nElems - Number of elements (array)
|
||||
*
|
||||
**************************************************************************-*/
|
||||
|
||||
#define PRN_TIME_VAL_STS(TYPE,TYPE_ENUM) \
|
||||
if (!tsInit) \
|
||||
{ /* Initialise reference timestamp */ \
|
||||
tsStart = tsPrev = ((struct TYPE *)value)->stamp; \
|
||||
tsInit = 1; \
|
||||
} \
|
||||
/* Print Timestamp */ \
|
||||
if (!firstStampPrinted) \
|
||||
{ /* First stamp is always absolute */ \
|
||||
printf("%s ", tsStampToText(&(((struct TYPE *)value)->stamp), \
|
||||
TS_TEXT_MMDDYY, timeText)); \
|
||||
firstStampPrinted = 1; \
|
||||
} else { \
|
||||
switch (tsType) { \
|
||||
case incTime: /* The following can be incremental, */ \
|
||||
printf("%10.4fs ", \
|
||||
tsDiffDbl( &(((struct TYPE *)value)->stamp), \
|
||||
&tsPrev) ); \
|
||||
break; \
|
||||
case relTime: /* relative, */ \
|
||||
printf("%10.4fs ", \
|
||||
tsDiffDbl( &(((struct TYPE *)value)->stamp), \
|
||||
&tsStart)); \
|
||||
break; \
|
||||
default : /* or also absolute */ \
|
||||
printf("%s ", \
|
||||
tsStampToText( &(((struct TYPE *)value)->stamp), \
|
||||
TS_TEXT_MMDDYY, timeText)); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
tsPrev = ((struct TYPE *)value)->stamp; \
|
||||
/* Print Values */ \
|
||||
for (i=0; i<nElems; ++i) { \
|
||||
printf ("%s ", val2str(value, TYPE_ENUM, i)); \
|
||||
} \
|
||||
/* Print Status, Severity - if not NO_ALARM */ \
|
||||
if ( ((struct TYPE *)value)->status || ((struct TYPE *)value)->severity ) \
|
||||
{ \
|
||||
printf("%s %s\n", \
|
||||
stat_to_str(((struct TYPE *)value)->status), \
|
||||
sevr_to_str(((struct TYPE *)value)->severity)); \
|
||||
} else { \
|
||||
printf("\n"); \
|
||||
}
|
||||
|
||||
|
||||
void print_time_val_sts (const pv* pv, int nElems)
|
||||
{
|
||||
char timeText[28];
|
||||
int i;
|
||||
void* value = pv->value;
|
||||
|
||||
printf("%-30s ", pv->name);
|
||||
if (pv->status == ECA_DISCONN)
|
||||
printf("*** not connected\n");
|
||||
else if (pv->status == ECA_NORDACCESS)
|
||||
printf("*** no read access\n");
|
||||
else if (pv->status != ECA_NORMAL)
|
||||
printf("*** CA error %s\n", ca_message(pv->status));
|
||||
else if (pv->value == 0)
|
||||
printf("*** no data available (timeout)\n");
|
||||
else
|
||||
switch (pv->dbrType) {
|
||||
case DBR_TIME_STRING:
|
||||
PRN_TIME_VAL_STS(dbr_time_string, DBR_TIME_STRING);
|
||||
break;
|
||||
case DBR_TIME_SHORT:
|
||||
PRN_TIME_VAL_STS(dbr_time_short, DBR_TIME_SHORT);
|
||||
break;
|
||||
case DBR_TIME_FLOAT:
|
||||
PRN_TIME_VAL_STS(dbr_time_float, DBR_TIME_FLOAT);
|
||||
break;
|
||||
case DBR_TIME_ENUM:
|
||||
PRN_TIME_VAL_STS(dbr_time_enum, DBR_TIME_ENUM);
|
||||
break;
|
||||
case DBR_TIME_CHAR:
|
||||
PRN_TIME_VAL_STS(dbr_time_char, DBR_TIME_CHAR);
|
||||
break;
|
||||
case DBR_TIME_LONG:
|
||||
PRN_TIME_VAL_STS(dbr_time_long, DBR_TIME_LONG);
|
||||
break;
|
||||
case DBR_TIME_DOUBLE:
|
||||
PRN_TIME_VAL_STS(dbr_time_double, DBR_TIME_DOUBLE);
|
||||
break;
|
||||
default: printf("can't print data type\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*+**************************************************************************
|
||||
*
|
||||
* Function: connect_pvs
|
||||
*
|
||||
* Description: Connects an arbitrary number of PVs
|
||||
*
|
||||
* Arg(s) In: pvs - Pointer to an array of pv structures
|
||||
* nPvs - Number of elements in the pvs array
|
||||
*
|
||||
* Arg(s) Out: none
|
||||
*
|
||||
* Return(s): Error code:
|
||||
* 0 - All PVs connected
|
||||
* 1 - Some PV(s) not connected
|
||||
*
|
||||
**************************************************************************-*/
|
||||
|
||||
int connect_pvs (pv* pvs, int nPvs)
|
||||
{
|
||||
int n;
|
||||
int result;
|
||||
int returncode = 0;
|
||||
/* Issue channel connections */
|
||||
for (n = 0; n < nPvs; n++) {
|
||||
result = ca_create_channel (pvs[n].name,
|
||||
0,
|
||||
0,
|
||||
CA_PRIORITY,
|
||||
&pvs[n].chid);
|
||||
if (result != ECA_NORMAL) {
|
||||
fprintf(stderr, "CA error %s occurred while trying "
|
||||
"to create channel '%s'.\n", ca_message(result), pvs[n].name);
|
||||
pvs[n].status = result;
|
||||
returncode = 1;
|
||||
}
|
||||
}
|
||||
/* Wait for channels to connect */
|
||||
result = ca_pend_io (timeout);
|
||||
if (result == ECA_TIMEOUT)
|
||||
{
|
||||
if (nPvs > 1)
|
||||
{
|
||||
fprintf(stderr, "Channel connect timed out: some PV(s) not found.\n");
|
||||
} else {
|
||||
fprintf(stderr, "Channel connect timed out: '%s' not found.\n",
|
||||
pvs[0].name);
|
||||
}
|
||||
returncode = 1;
|
||||
}
|
||||
|
||||
return returncode;
|
||||
}
|
||||
71
src/catools/tool_lib.h
Normal file
71
src/catools/tool_lib.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer
|
||||
* Synchrotronstrahlung.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Author: Ralph Lange (BESSY)
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INCLtool_libh
|
||||
#define INCLtool_libh
|
||||
|
||||
/* Convert status and severity to strings */
|
||||
#define stat_to_str(stat) \
|
||||
((stat) >= 0 && (stat) <= (signed)lastEpicsAlarmCond) ? \
|
||||
epicsAlarmConditionStrings[stat] : "??"
|
||||
|
||||
#define sevr_to_str(stat) \
|
||||
((stat) >= 0 && (stat) <= (signed)lastEpicsAlarmSev) ? \
|
||||
epicsAlarmSeverityStrings[stat] : "??"
|
||||
|
||||
|
||||
#define CA_PRIORITY 50 /* CA priority */
|
||||
#define DEFAULT_TIMEOUT 1.0 /* Default CA timeout */
|
||||
|
||||
|
||||
/* Type of timestamp (absolute, relative, incremental) */
|
||||
typedef enum { absTime, relTime, incTime } TimeT;
|
||||
|
||||
/* Output formats for integer data types */
|
||||
typedef enum { dec, bin, oct, hex } IntFormatT;
|
||||
|
||||
/* Structure representing one PV (= channel) */
|
||||
typedef struct
|
||||
{
|
||||
char* name;
|
||||
chid chid;
|
||||
long dbfType;
|
||||
long dbrType;
|
||||
unsigned long nElems;
|
||||
unsigned long reqElems;
|
||||
int status;
|
||||
void* value;
|
||||
} pv;
|
||||
|
||||
|
||||
extern TimeT tsType; /* Timestamp type flag (-q or -Q option) */
|
||||
extern IntFormatT outType; /* Flag used for -0.. output format option */
|
||||
extern int charAsNr; /* Used for -n option (get DBF_CHAR as number) */
|
||||
extern double timeout; /* Wait time default (see -w option) */
|
||||
extern char dblFormatStr[]; /* Format string to print doubles (see -e -f option) */
|
||||
|
||||
|
||||
extern char *val2str (const void *v, unsigned type, int index);
|
||||
extern char *dbr2str (const void *value, unsigned type);
|
||||
extern void print_time_val_sts (const pv *pv, int nElems);
|
||||
extern int connect_pvs (pv *pvs, int nPvs);
|
||||
|
||||
/*
|
||||
* no additions below this endif
|
||||
*/
|
||||
#endif /* ifndef INCLtool_libh */
|
||||
Reference in New Issue
Block a user