diff --git a/src/catools/Makefile b/src/catools/Makefile index bdeb693c7..4673dc32d 100644 --- a/src/catools/Makefile +++ b/src/catools/Makefile @@ -25,10 +25,12 @@ 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 +PROD_HOST += caget camonitor caget_SRCS += caget.c -caget_LIBS += catools ca Com +camonitor_SRCS += camonitor.c + +PROD_LIBS += catools ca Com catools_DIR=. diff --git a/src/catools/camonitor.c b/src/catools/camonitor.c new file mode 100644 index 000000000..b6390a476 --- /dev/null +++ b/src/catools/camonitor.c @@ -0,0 +1,332 @@ +/*************************************************************************\ +* 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 +#include +#include + +#include +#include +#include +#include + +#include "tool_lib.h" + +#define VALID_DOUBLE_DIGITS 18 /* Max usable precision for a double */ + +static int nConn = 0; /* Number of connected PVs */ + /* Event mask used */ +static unsigned long eventMask = DBE_VALUE | DBE_ALARM; + + +void usage (void) +{ + fprintf (stderr, "\nUsage: camonitor [options] ...\n\n" + " -h: Help: Print this message\n" + "Channel Access options:\n" + " -w : Wait time, specifies longer CA timeout, default is %f second\n" + " -m : Specify CA event mask to use, with being any combination of\n" + " 'v' (value), 'a' (alarm), 'l' (log). Default: va\n" + "Enum format:\n" + " -n: Print DBF_ENUM values as number (default are enum string values)\n" + "Arrays: Value format: print number of requested values, then list of values\n" + " Default: Print all values\n" + " -# : Print first elements of an array\n" + "Floating point type format:\n" + " Default: Use g format\n" + " -f : Use f format, with digits after the decimal point\n" + " -e : Use e format, with 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: camonitor -f8 my_channel another_channel\n" + " (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; + memcpy(pv->value, args.dbr, dbr_size_n(args.type, args.count)); + + print_time_val_sts(pv, pv->reqElems); + } +} + + + +/*+************************************************************************** + * + * Function: camonitor + * + * 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 + * reqElems - Requested number of (array) elements + * + * Return(s): Error code: 0 = OK, 1 = Error + * + **************************************************************************-*/ + +int camonitor (pv *pvs, int nPvs, unsigned long reqElems) +{ + int n, result; + chtype dbrType; + + 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 */ + 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++; + /* Allocate value structure */ + pvs[n].value = calloc(1, dbr_size_n(dbrType, reqElems)); + + result = ca_create_subscription(dbrType, + reqElems, + pvs[n].chid, + eventMask, + event_handler, + (void*)&pvs[n], + NULL); + pvs[n].status = result; + } else { + pvs[n].status = ECA_DISCONN; + } + } + if (!nConn) return 1; /* No connection? We're done. */ + + /* Wait for callbacks */ + /* ---------------- */ + while (1) + ca_pend_event(timeout); + + return 0; +} + + + +/*+************************************************************************** + * + * Function: main + * + * Description: camonitor main() + * Evaluate command line options, set up CA, connect the + * channels, collect and print the data as requested + * + * Arg(s) In: [options] ... + * + * Arg(s) Out: none + * + * Return(s): Standard return code (0=success, 1=error) + * + **************************************************************************-*/ + +int main (int argc, char *argv[]) +{ + int n = 0; + int result; /* CA result */ + + int count = 0; /* 0 = not specified by -# option */ + int opt; /* getopt() current option */ + 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, ":nhm:e:f:#:d:0:w:")) != -1) { + switch (opt) { + case 'h': /* Print usage */ + usage(); + return 0; + 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 'm': /* Select CA event mask */ + eventMask = 0; + { + int i = 0; + char c, err = 0; + while ((c = optarg[i++]) && !err) + switch (c) { + case 'v': eventMask |= DBE_VALUE; break; + case 'a': eventMask |= DBE_ALARM; break; + case 'l': eventMask |= DBE_LOG; break; + default : + fprintf(stderr, "Invalid argument '%s' " + "for option '-m' - ignored.\n", optarg); + eventMask = DBE_VALUE | DBE_ALARM; + err = 1; + } + } + break; + case 'e': /* Select %e format, using 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 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 */ + 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. ('camonitor -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 = camonitor(pvs, nPvs, count); + + /* Shut down Channel Access */ + ca_context_destroy(); + + return result; +}