67#include <epicsVersion.h>
68#if (EPICS_VERSION > 3)
70# include <boost/algorithm/string.hpp>
75#include "match_string.h"
81#define CLO_PENDIOTIME 2
84#define CLO_EZCATIMING 4
86#define CLO_DELTAMODE 6
87#define CLO_NUMERICAL 7
88#define CLO_BLUNDERAHEAD 8
90#define CLO_PROVIDER 10
91#define CLO_CHARARRAY 11
92#define COMMANDLINE_OPTIONS 12
93char *commandline_option[COMMANDLINE_OPTIONS] = {
102 (
char *)
"blunderahead",
109#define PROVIDER_PVA 1
110#define PROVIDER_COUNT 2
111char *providerOption[PROVIDER_COUNT] = {
116static char *USAGE = (
char *)
118 " [-list=<string>[=<value>][,<string>[=<value>]...]]\n"
119 " [-range=begin=<integer>,end=<integer>[,format=<string>][,interval=<integer>]]\n"
120 " [-pendIoTime=<seconds>]\n"
121 " [-deltaMode[=factor=<value>]]\n"
122 " [-ramp=step=<n>,pause=<sec>]\n"
125 " [-blunderAhead[=silently]]\n"
126 " [-provider={ca|pva}]\n"
128 " -list Specifies PV name string components.\n"
129 " -range Specifies range of integers and format string.\n"
130 " -pendIoTime Maximum time to wait for connections and return values. Default: 1.0s.\n"
131 " -dryRun Shows PV names and values without sending them to IOCs.\n"
132 " -deltaMode Specifies values are deltas from present PV values.\n"
133 " -numerical Forces value conversion to a number for sending. Default for -deltaMode.\n"
134 " -charArray Use when passing a string to a char array PV.\n"
135 " -ramp Ramp to value using steps (default: 1) with pauses (default: 0.1s).\n"
136 " -blunderAhead Executes all possible puts, even if some PVs don't connect.\n"
137 " -provider Specifies provider; defaults to ca (Channel Access).\n"
138 "\nProgram by Michael Borland and Robert Soliday, ANL/APS (" EPICS_VERSION_STRING
", " __DATE__
").\n";
140#define DO_BLUNDERAHEAD 0x0001U
141#define BLUNDER_SILENTLY 0x0002U
142void oag_ca_exception_handler(
struct exception_handler_args args);
144#if (EPICS_VERSION > 3)
148int main(
int argc,
char **argv) {
149 int32_t beginRange, endRange, rangeInterval, rampSteps;
151 long PVs, j, i_arg, numerical, iramp;
152 unsigned long blunderAhead;
155 unsigned long flags, dummyFlags;
157 char *ptr, *rangeFormat;
159 chid *channelID = NULL;
160 short deltaMode, doRamp, charArray=0;
161 double *presentValue, deltaFactor, rampPause;
162 long providerMode = 0;
163#if (EPICS_VERSION > 3)
165 double *rampDeltas = NULL;
168 argc =
scanargs(&s_arg, argc, argv);
177 deltaMode = numerical = 0;
184 for (i_arg = 1; i_arg < argc; i_arg++) {
185 if (s_arg[i_arg].arg_type == OPTION) {
187 switch (
match_string(s_arg[i_arg].list[0], commandline_option, COMMANDLINE_OPTIONS, 0)) {
189 if (s_arg[i_arg].n_items < 2)
190 SDDS_Bomb((
char *)
"invalid -list syntax (cavput)");
192 for (j = 1; j < s_arg[i_arg].n_items; j++) {
193 List[j - 1].flags = 0;
194 if ((ptr = strchr(s_arg[i_arg].list[j],
'=')))
196 List[j - 1].string = s_arg[i_arg].list[j];
198 List[j - 1].flags |= VALUE_GIVEN;
199 List[j - 1].value = ptr;
202 multiplyWithList(&PVvalue, &PVs, List, s_arg[i_arg].n_items - 1);
205 s_arg[i_arg].n_items--;
208 if (!
scanItemList(&flags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
209 "begin",
SDDS_LONG, &beginRange, 1, BEGIN_GIVEN,
210 "end",
SDDS_LONG, &endRange, 1, END_GIVEN,
211 "interval",
SDDS_LONG, &rangeInterval, 1, INTERVAL_GIVEN,
212 "format",
SDDS_STRING, &rangeFormat, 1, FORMAT_GIVEN,
214 !(flags & BEGIN_GIVEN) || !(flags & END_GIVEN) || beginRange > endRange ||
216 SDDS_Bomb((
char *)
"invalid -range syntax/values");
218 rangeFormat = (
char *)
"%ld";
219 multiplyWithRange(&PVvalue, &PVs, beginRange, endRange, rangeInterval, rangeFormat);
220 s_arg[i_arg].n_items++;
223 if (s_arg[i_arg].n_items != 2)
224 bomb((
char *)
"wrong number of items for -pendIoTime", NULL);
225 if (sscanf(s_arg[i_arg].list[1],
"%lf", &pendIOTime) != 1 ||
227 SDDS_Bomb((
char *)
"invalid -pendIoTime value (cavget)");
233 fprintf(stderr,
"warning (cavput): -ezcaTiming option is obsolete. Use -pendIoTime\n");
236 fprintf(stderr,
"warning (cavput): -noGroups option is obsolete.\n");
240 s_arg[i_arg].n_items--;
241 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
244 SDDS_Bomb((
char *)
"invalid -deltaMode syntax/values");
245 s_arg[i_arg].n_items++;
253 case CLO_BLUNDERAHEAD:
254 s_arg[i_arg].n_items--;
255 if (!
scanItemList(&blunderAhead, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
256 "silently", -1, NULL, 0, BLUNDER_SILENTLY,
258 SDDS_Bomb((
char *)
"invalid -blunderAhead syntax/values");
259 blunderAhead |= DO_BLUNDERAHEAD;
260 s_arg[i_arg].n_items++;
263 s_arg[i_arg].n_items--;
266 if (!
scanItemList(&flags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
269 (rampSteps <= 1) || (rampPause < 0))
270 SDDS_Bomb((
char *)
"invalid -ramp syntax/values");
272 s_arg[i_arg].n_items++;
275 if (s_arg[i_arg].n_items != 2)
276 SDDS_Bomb((
char *)
"no value given for option -provider");
277 if ((providerMode =
match_string(s_arg[i_arg].list[1], providerOption,
278 PROVIDER_COUNT, 0)) < 0)
282 bomb((
char *)
"unknown option (cavput)", NULL);
286 bomb((
char *)
"unknown option", NULL);
289 for (j = 0; j < PVs; j++)
290 if (!PVvalue[j].value) {
291 fprintf(stderr,
"Error: no value given for %s\n", PVvalue[j].name);
296 for (j = 0; j < PVs; j++) {
297 if (strncmp(PVvalue[j].name,
"pva://", 6) == 0) {
298 PVvalue[j].name += 6;
299 printf(
"%32s %s\n", PVvalue[j].name, PVvalue[j].value);
300 PVvalue[j].name -= 6;
301 }
else if (strncmp(PVvalue[j].name,
"ca://", 5) == 0) {
302 PVvalue[j].name += 5;
303 printf(
"%32s %s\n", PVvalue[j].name, PVvalue[j].value);
304 PVvalue[j].name -= 5;
306 printf(
"%32s %s\n", PVvalue[j].name, PVvalue[j].value);
310#if (EPICS_VERSION > 3)
312 allocPVA(&pva, PVs, 0);
314 epics::pvData::shared_vector<std::string> names(pva.numPVs);
315 epics::pvData::shared_vector<std::string> provider(pva.numPVs);
316 for (j = 0; j < pva.numPVs; j++) {
317 if (strncmp(PVvalue[j].name,
"pva://", 6) == 0) {
318 PVvalue[j].name += 6;
319 names[j] = PVvalue[j].name;
320 PVvalue[j].name -= 6;
322 }
else if (strncmp(PVvalue[j].name,
"ca://", 5) == 0) {
323 PVvalue[j].name += 5;
324 names[j] = PVvalue[j].name;
325 PVvalue[j].name -= 5;
328 names[j] = PVvalue[j].name;
329 if (providerMode == PROVIDER_PVA) {
336 pva.pvaChannelNames = freeze(names);
337 pva.pvaProvider = freeze(provider);
339 ConnectPVA(&pva, pendIOTime);
341 for (j = 0; j < PVs; j++) {
342 if (!pva.isConnected[j]) {
343 if (!(blunderAhead & BLUNDER_SILENTLY))
344 fprintf(stderr,
"Channel not connected: %s\n", PVvalue[j].name);
345 if (!blunderAhead && (doRamp || deltaMode))
353 if (GetPVAValues(&pva) == 1) {
354 return (EXIT_FAILURE);
358 if (doRamp || deltaMode) {
359 for (j = 0; j < PVs; j++) {
360 if (pva.isConnected[j]) {
361 if (pva.pvaData[j].fieldType != epics::pvData::scalar) {
362 fprintf(stderr,
"error (cavput): %s is not a scalar type PV, delta and ramp options are only available for scalar type PVs\n", PVvalue[j].name);
363 return (EXIT_FAILURE);
369 if (PrepPVAPutValues(&pva, PVvalue, charArray)) {
370 return (EXIT_FAILURE);
375 for (j = 0; j < PVs; j++) {
376 if (pva.isConnected[j] && (pva.pvaData[j].fieldType == epics::pvData::scalar) && pva.pvaData[j].numeric) {
377 pva.pvaData[j].putData[0].values[0] = pva.pvaData[j].putData[0].values[0] * deltaFactor + pva.pvaData[j].getData[0].values[0];
385 rampDeltas = (
double *)malloc(
sizeof(
double) * PVs);
386 fValues = (
double *)malloc(
sizeof(
double) * PVs);
387 for (j = 0; j < PVs; j++) {
388 if (pva.isConnected[j] && (pva.pvaData[j].fieldType == epics::pvData::scalar) && pva.pvaData[j].numeric) {
389 fValues[j] = pva.pvaData[j].putData[0].values[0];
390 rampDeltas[j] = (pva.pvaData[j].putData[0].values[0] - pva.pvaData[j].getData[0].values[0]) / rampSteps;
391 pva.pvaData[j].putData[0].values[0] = pva.pvaData[j].getData[0].values[0];
395 for (iramp = 1; iramp <= rampSteps; iramp++) {
396 for (j = 0; j < PVs; j++) {
397 if (pva.isConnected[j] && (pva.pvaData[j].fieldType == epics::pvData::scalar) && pva.pvaData[j].numeric) {
398 if (iramp == rampSteps) {
399 pva.pvaData[j].putData[0].values[0] = fValues[j];
401 pva.pvaData[j].putData[0].values[0] = pva.pvaData[j].putData[0].values[0] + rampDeltas[j];
403 pva.pvaData[j].numPutElements = 1;
406 if (PutPVAValues(&pva) == 1) {
407 return (EXIT_FAILURE);
409 if ((rampPause > 0) && (iramp < rampSteps)) {
416 if (PutPVAValues(&pva) == 1) {
417 return (EXIT_FAILURE);
422 for (j = 0; j < PVs; j++) {
424 free(PVvalue[j].name);
425 if (PVvalue[j].value)
426 free(PVvalue[j].value);
434 return (EXIT_SUCCESS);
436 ca_task_initialize();
437 ca_add_exception_event(oag_ca_exception_handler, NULL);
438 if (!(channelID = (chid *)malloc(
sizeof(*channelID) * PVs)))
439 bomb((
char *)
"memory allocation failure (cavput)", NULL);
440 for (j = 0; j < PVs; j++) {
441 if (ca_search(PVvalue[j].name, channelID + j) != ECA_NORMAL) {
442 fprintf(stderr,
"error (cavput): problem doing search for %s\n",
448 fprintf(stderr,
"ca_search initiated for %s\n", PVvalue[j].name);
451 ca_pend_io(pendIOTime);
453 fprintf(stderr,
"ca_pend_io returned\n");
455 if (doRamp || deltaMode) {
456 for (j = 0; j < PVs; j++) {
457 if (ca_state(channelID[j]) != cs_conn) {
458 if (!(blunderAhead & BLUNDER_SILENTLY))
459 fprintf(stderr,
"error (cavput): no connection for %s\n",
467 if (!(presentValue = (
double *)malloc(
sizeof(*presentValue) * PVs))) {
468 fprintf(stderr,
"error (cavput): memory allocation failure\n");
472 for (j = 0; j < PVs; j++) {
473 presentValue[j] = DBL_MAX;
474 if (ca_state(channelID[j]) != cs_conn)
476 if (ca_get(DBR_DOUBLE, channelID[j], presentValue + j) != ECA_NORMAL) {
477 fprintf(stderr,
"error (cavput): problem doing get for %s\n",
483 ca_pend_io(pendIOTime);
484 for (j = 0; j < PVs; j++) {
485 if (ca_state(channelID[j]) != cs_conn)
487 if (presentValue[j] == DBL_MAX) {
488 fprintf(stderr,
"error (cavput): no value returned in time for %s\n",
493 if (sscanf(PVvalue[j].value,
"%le", &PVvalue[j].numericalValue) != 1) {
494 fprintf(stderr,
"error (cavput): value (%s) for %s is not numerical--can't use -deltaMode or -ramp\n",
495 PVvalue[j].value, PVvalue[j].name);
503 for (j = 0; j < PVs; j++) {
504 PVvalue[j].numericalValue = PVvalue[j].numericalValue * deltaFactor + presentValue[j];
509 fprintf(stderr,
"doing non-ramped puts\n");
511 for (j = 0; j < PVs; j++) {
512 if (ca_state(channelID[j]) != cs_conn) {
513 if (!(blunderAhead & BLUNDER_SILENTLY))
514 fprintf(stderr,
"Channel not connected: %s\n", PVvalue[j].name);
518 fprintf(stderr,
"%s is connected\n", PVvalue[j].name);
520 if (ca_write_access(channelID[j]) == 0) {
521 fprintf(stderr,
"Write access denied on : %s\n", PVvalue[j].name);
524 if (ca_put(DBR_STRING, channelID[j], PVvalue[j].value) != ECA_NORMAL) {
525 if (!(blunderAhead & BLUNDER_SILENTLY))
526 fprintf(stderr,
"error (cavput): problem doing put for %s\n",
534 fprintf(stderr,
"ca_put of %s for %s done\n", PVvalue[j].value, PVvalue[j].name);
538 if (sscanf(PVvalue[j].value,
"%le", &PVvalue[j].numericalValue) != 1) {
540 "error (cavput): value (%s) for %s is not scannable as a number---can't use -numerical.\n",
541 PVvalue[j].value, PVvalue[j].name);
546 if (ca_put(DBR_DOUBLE, channelID[j], &PVvalue[j].numericalValue) != ECA_NORMAL) {
547 if (!(blunderAhead & BLUNDER_SILENTLY))
548 fprintf(stderr,
"error (cavput): problem doing put for %s\n",
556 fprintf(stderr,
"ca_put of %f for %s done\n", PVvalue[j].numericalValue, PVvalue[j].name);
562 for (iramp = 0; iramp < rampSteps; iramp++) {
563 for (j = 0; j < PVs; j++) {
564 rampDelta = (PVvalue[j].numericalValue - presentValue[j]) / rampSteps;
565 rampValue = presentValue[j] + (iramp + 1) * rampDelta;
566 if (ca_put(DBR_DOUBLE, channelID[j], &rampValue) != ECA_NORMAL) {
567 if (!(blunderAhead & BLUNDER_SILENTLY))
568 fprintf(stderr,
"error (cavput): problem doing put for %s\n",
576 if ((status = ca_pend_io(pendIOTime)) != ECA_NORMAL) {
577 fprintf(stderr,
"Problem processing one or more puts (cavput):\n%s\n",
583 ca_pend_event(rampPause);
590 if ((status = ca_pend_io(pendIOTime)) != ECA_NORMAL) {
591 fprintf(stderr,
"Problem processing one or more puts (cavput):\n%s\n",
598 fprintf(stderr,
"ca_pend_io returned\n");
603 fprintf(stderr,
"ca_task_exit returned\n");
610 fprintf(stderr,
"starting to free memory\n");
616 fprintf(stderr,
"free'd channelID\n");
619 for (j = 0; j < PVs; j++) {
621 free(PVvalue[j].name);
623 PVvalue[j].name = PVvalue[j].value = NULL;
627 fprintf(stderr,
"free'd PVvalue lists\n");
635 fprintf(stderr,
"free_scanargs returned\n");
641 fprintf(stderr,
"free'd List\n");
645 fprintf(stderr,
"exiting main routine\n");
648 return (EXIT_SUCCESS);
651void oag_ca_exception_handler(
struct exception_handler_args args) {
654 static const char *severity[] =
665 severityInt = CA_EXTRACT_SEVERITY(args.stat);
667 if ((severityInt != 1) && (severityInt != 3)) {
668 fprintf(stderr,
"CA.Client.Exception................\n");
669 fprintf(stderr,
" %s: \"%s\"\n",
670 severity[severityInt],
671 ca_message(args.stat));
674 fprintf(stderr,
" Context: \"%s\"\n", args.ctx);
677 pName = (
char *)ca_name(args.chid);
678 fprintf(stderr,
" Channel: \"%s\"\n", pName);
679 fprintf(stderr,
" Type: \"%s\"\n", dbr_type_to_text(args.type));
681 fprintf(stderr,
"This sometimes indicates an IOC that is hung up.\n");
684 fprintf(stdout,
"CA.Client.Exception................\n");
685 fprintf(stdout,
" %s: \"%s\"\n",
686 severity[severityInt],
687 ca_message(args.stat));
690 fprintf(stdout,
" Context: \"%s\"\n", args.ctx);
693 pName = (
char *)ca_name(args.chid);
694 fprintf(stdout,
" Channel: \"%s\"\n", pName);
695 fprintf(stdout,
" Type: \"%s\"\n", dbr_type_to_text(args.type));
700#if (EPICS_VERSION > 3)
703 for (j = 0; j < pva->numPVs; j++) {
704 if (pva->isConnected[j]) {
705 switch (pva->pvaData[j].fieldType) {
706 case epics::pvData::scalar: {
707 pva->pvaData[j].numPutElements = 1;
708 if (pva->pvaData[j].numeric) {
709 pva->pvaData[j].putData[0].values = (
double *)malloc(
sizeof(
double));
710 if (sscanf(PVvalue[j].value,
"%le", &(pva->pvaData[j].putData[0].values[0])) != 1) {
711 fprintf(stderr,
"error (cavput): value (%s) for %s is not numerical\n", PVvalue[j].value, PVvalue[j].name);
715 pva->pvaData[j].putData[0].stringValues = (
char **)malloc(
sizeof(
char *));
716 pva->pvaData[j].putData[0].stringValues[0] = (
char *)malloc(
sizeof(
char) * (strlen(PVvalue[j].value) + 1));
717 strcpy(pva->pvaData[j].putData[0].stringValues[0],
trim_spaces(PVvalue[j].value));
721 case epics::pvData::scalarArray: {
723 size_t length = strlen(PVvalue[j].value);
724 pva->pvaData[j].numPutElements = count_chars(PVvalue[j].value,
',') + 1;
725 if (pva->pvaData[j].numeric) {
728 pva->pvaData[j].numPutElements = 0;
731 pva->pvaData[j].numPutElements = strlen(PVvalue[j].value) + 1;
732 pva->pvaData[j].putData[0].values = (
double *)malloc(
sizeof(
double) * pva->pvaData[j].numPutElements);
733 for (i = 0; i < pva->pvaData[j].numPutElements - 1; i++) {
734 pva->pvaData[j].putData[0].values[i] = (int)(PVvalue[j].value[i]);
736 pva->pvaData[j].putData[0].values[pva->pvaData[j].numPutElements - 1] = 0;
738 if (pva->pvaData[j].numPutElements > 0) {
739 ptr = PVvalue[j].value;
740 pva->pvaData[j].putData[0].values = (
double *)malloc(
sizeof(
double) * pva->pvaData[j].numPutElements);
741 for (i = 0; i < pva->pvaData[j].numPutElements; i++) {
742 if ((ptr2 = strchr(ptr,
',')) != NULL) {
749 if (sscanf(ss.c_str(),
"%le", &(pva->pvaData[j].putData[0].values[i])) != 1) {
750 fprintf(stderr,
"error (cavput): value (%s) for %s is not numerical\n", ss.c_str(), PVvalue[j].name);
758 if ((length == 0) && (pva->pvaData[j].scalarType != epics::pvData::pvString)) {
759 pva->pvaData[j].numPutElements = 0;
761 if (pva->pvaData[j].numPutElements > 0) {
762 ptr = PVvalue[j].value;
763 pva->pvaData[j].putData[0].stringValues = (
char **)malloc(
sizeof(
char *) * pva->pvaData[j].numPutElements);
764 for (i = 0; i < pva->pvaData[j].numPutElements; i++) {
765 if ((ptr2 = strchr(ptr,
',')) != NULL) {
773 pva->pvaData[j].putData[0].stringValues[i] = (
char *)malloc(
sizeof(
char) * (ss.length() + 1));
774 strcpy(pva->pvaData[j].putData[0].stringValues[i], ss.c_str());
780 case epics::pvData::structure: {
781 if (pva->pvaData[j].pvEnumeratedStructure) {
782 pva->pvaData[j].numPutElements = 1;
783 pva->pvaData[j].putData[0].stringValues = (
char **)malloc(
sizeof(
char *));
784 pva->pvaData[j].putData[0].stringValues[0] = (
char *)malloc(
sizeof(
char) * (strlen(PVvalue[j].value) + 1));
785 strcpy(pva->pvaData[j].putData[0].stringValues[0],
trim_spaces(PVvalue[j].value));
786 pva->pvaData[j].putData[0].values = (
double *)malloc(
sizeof(
double));
787 pva->pvaData[j].putData[0].values = 0;
789 std::cerr <<
"error (cavput): unexpected structure type " << std::endl;
795 std::cerr <<
"error (cavput): unexpected field type " << pva->pvaData[j].fieldType << std::endl;
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_StringIsBlank(char *s)
Checks if a string is blank (contains only whitespace characters).
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
#define SDDS_STRING
Identifier for the string data type.
#define SDDS_LONG
Identifier for the signed 32-bit integer data type.
#define SDDS_DOUBLE
Identifier for the double data type.
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
char * delete_chars(char *s, char *t)
Removes all occurrences of characters found in string t from string s.
void usleepSystemIndependent(long usec)
Sleep for a given number of microseconds, system-independently.
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
Helper Functions used by cavget and cavput.
Functions for managing and interacting with Process Variable Array (PVA) structures.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
void free_scanargs(SCANNED_ARG **scanned, int argc)
long scanItemList(unsigned long *flags, char **item, long *items, unsigned long mode,...)
Scans a list of items and assigns values based on provided keywords and types.
char * trim_spaces(char *s)
Trims leading and trailing spaces from a string.