SDDS ToolKit Programs and Libraries for C and Python
All Classes Files Functions Variables Macros Pages
cawait.cc
Go to the documentation of this file.
1/**
2 * @file cawait.cc
3 * @brief Wait for Channel Access (CA) and pvAccess (PVA) Process Variable (PV) values to satisfy specific conditions from the command line.
4 *
5 * @details
6 * This program waits for a set of PVs to satisfy specific conditions. It provides logical operations on
7 * PV states, supports multiple event triggers, and enables the execution of commands during various event phases.
8 * It utilizes both Channel Access (CA) and PV Access (PVA) protocols for PV interaction.
9 *
10 * @section Usage
11 * ```
12 * cawait [-interval=<seconds>]
13 * [-timeLimit=<seconds>]
14 * [-totalTimeLimit=<seconds>]
15 * [-interval=<seconds>]
16 * [-timeLimit=<seconds>]
17 * [-totalTimeLimit=<seconds>]
18 * -waitFor=<PVname>[,lowerLimit=<value>,upperLimit=<value>]
19 * [,equalTo=<value>][,sameAs=<string>][,above=<value>
20 * [,below=<value>][,changed][,monitor]
21 * [{-and | -or | -not}]
22 * [-repeat[=<number>]]
23 * [-emit=event=<string>[,timeout=<string>][,end=<string>]]
24 * [-preEvent=<command>]
25 * [-onEvent=<command>]
26 * [-postEvent=<command>]
27 * [-onEnd=<command>]
28 * [-subprocess=<command>[,event=<string>][,timeout=<string>][,end=<string>]]
29 * [-pendIOTime=<seconds>]
30 * [-noWarnings]
31 * [-provider={ca|pva}]
32 * ```
33 *
34 * @section Options
35 * | Required | Description |
36 * |---------------------------------------|---------------------------------------------------------------------------------------|
37 * | `-waitFor` | Specifies a PV and conditions to monitor. |
38 *
39 * | Optional | Description |
40 * |---------------------------------------|---------------------------------------------------------------------------------------|
41 * | `-interval` | Interval for PV state checks (default: 0.1 s). |
42 * | `-timeLimit` | Maximum wait time for a single event (default: infinite). |
43 * | `-totalTimeLimit` | Maximum runtime for the program (default: infinite). |
44 * | `-repeat` | Number of times to repeat the monitoring process (default: 1) (0=infinite). |
45 * | `-emit` | Strings emitted during event or timeout detection. |
46 * | `-preEvent`, `-postEvent` | Commands to execute before and after monitoring. |
47 * | `-onEvent`, `-onEnd` | Commands executed during event detection or after completion. |
48 * | `-subprocess` | Subprocess to execute with optional event or timeout messages. |
49 * | `-pendIOTime` | Time limit for Channel Access (CA) operations. |
50 * | `-provider` | Protocol provider for PV monitoring (default: CA). |
51 * | `-noWarnings` | Suppresses warnings during execution. |
52 *
53 * @copyright
54 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
55 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
56 *
57 * @license
58 * This file is distributed under the terms of the Software License Agreement
59 * found in the file LICENSE included with this distribution.
60 *
61 * @authors
62 * - M. Borland
63 * - R. Soliday
64 */
65#include <complex>
66#include <iostream>
67#include <queue>
68#include <cstdlib>
69#include <ctime>
70#include <cstddef>
71
72#if defined(_WIN32)
73# include <winsock.h>
74#else
75# include <unistd.h>
76#endif
77
78#include <cadef.h>
79#include <epicsVersion.h>
80#if (EPICS_VERSION > 3)
81# include "pvaSDDS.h"
82# include "pv/thread.h"
83#endif
84
85#include "mdb.h"
86#include "scan.h"
87#include "match_string.h"
88#include "SDDS.h"
89
90#define CLO_INTERVAL 0
91#define CLO_WAITFOR 1
92#define CLO_EZCATIMING 2
93#define CLO_TIMELIMIT 3
94#define CLO_USEMONITOR 4
95#define CLO_NOT 5
96#define CLO_OR 6
97#define CLO_AND 7
98#define CLO_REPEAT 8
99#define CLO_EMIT 9
100#define CLO_PREEVENT 10
101#define CLO_POSTEVENT 11
102#define CLO_SUBPROCESS 12
103#define CLO_ONEVENT 13
104#define CLO_ONEND 14
105#define CLO_TOTALTIMELIMIT 15
106#define CLO_PENDIOTIME 16
107#define CLO_NOWARN 17
108#define CLO_PROVIDER 18
109#define COMMANDLINE_OPTIONS 19
110char *commandline_option[COMMANDLINE_OPTIONS] = {
111 (char *)"interval", (char *)"waitfor", (char *)"ezcatiming",
112 (char *)"timelimit", (char *)"usemonitor", (char *)"not",
113 (char *)"or", (char *)"and", (char *)"repeat", (char *)"emit",
114 (char *)"preevent", (char *)"postevent", (char *)"subprocess",
115 (char *)"onevent", (char *)"onend", (char *)"totaltimelimit",
116 (char *)"pendiotime", (char *)"nowarnings", (char *)"provider"};
117
118#define PROVIDER_CA 0
119#define PROVIDER_PVA 1
120#define PROVIDER_COUNT 2
121char *providerOption[PROVIDER_COUNT] = {
122 (char *)"ca",
123 (char *)"pva",
124};
125
126static char *USAGE = (char *)
127 "cawait\n"
128 " [-interval=<seconds>]\n"
129 " [-timeLimit=<seconds>]\n"
130 " [-totalTimeLimit=<seconds>]\n"
131 " -waitFor=<PVname>[,lowerLimit=<value>,upperLimit=<value>]\n"
132 " [,equalTo=<value>][,sameAs=<string>][,above=<value>]\n"
133 " [,below=<value>][,changed][,monitor]\n"
134 " [{-and | -or | -not}]\n"
135 " [-repeat[=<number>]]\n"
136 " [-emit=event=<string>[,timeout=<string>][,end=<string>]]\n"
137 " [-preEvent=<command>]\n"
138 " [-onEvent=<command>]\n"
139 " [-postEvent=<command>]\n"
140 " [-onEnd=<command>]\n"
141 " [-subprocess=<command>[,event=<string>][,timeout=<string>][,end=<string>]]\n"
142 " [-pendIOTime=<seconds>]\n"
143 " [-noWarnings]\n"
144 " [-provider={ca|pva}]\n"
145 "\n"
146 "Description of options:\n"
147 " -interval Specifies interval between checking PVs (default: 0.1 s).\n"
148 " Uses monitors, so it doesn’t load the IOCs.\n"
149 " -timeLimit Specifies maximum time to wait (default: infinite).\n"
150 " -totalTimeLimit Specifies the maximum runtime of cawait (default: infinite).\n"
151 " -waitFor Specifies PV name and criteria for ending wait.\n"
152 " Each occurrence pushes a result on the logic stack.\n"
153 " -and, -or, -not Performs operations on the logic stack.\n"
154 " -repeat Specifies the number of times to wait for the event (default: 1).\n"
155 " If no qualifiers, defaults to infinite. Event condition must\n"
156 " become false to start a new event.\n"
157 " -emit Specifies strings to emit to stdout when the event condition,\n"
158 " a timeout, or the specified number of events is detected.\n"
159 " -preEvent Command executed before waiting for the event.\n"
160 " -onEvent Command executed when the event occurs.\n"
161 " -postEvent Command executed after waiting for the event.\n"
162 " -onEnd Command executed after the final event is detected.\n"
163 " -subprocess Command executed on a pipe; optionally specifies messages for\n"
164 " events, timeouts, and end-of-run.\n"
165 " -pendIOTime Sets time (in seconds) allowed for CA operations.\n"
166 " -provider Specifies the provider (default: ca - channel access).\n"
167 "\n"
168 "Program by Michael Borland and Robert Soliday, ANL/APS (" EPICS_VERSION_STRING ", " __DATE__ ")";
169
170#define LOWERLIMIT_GIVEN 0x00001U
171#define UPPERLIMIT_GIVEN 0x00002U
172#define EQUALTO_GIVEN 0x00004U
173#define ABOVE_GIVEN 0x00008U
174#define BELOW_GIVEN 0x00010U
175#define SAME_AS_GIVEN 0x00020U
176#define CHANGED_GIVEN 0x00040U
177#define MONITOR_GIVEN 0x00080U
178
179#define OP_NOT CLO_NOT
180#define OP_OR CLO_OR
181#define OP_AND CLO_AND
182
183typedef struct
184{
185 char *PVname;
186 chid channelID;
187 long channelType;
188 double lowerLimit, upperLimit, equalTo;
189 double above, below, changeReference;
190 long operations;
191 short *operation;
192 char *sameAs;
193 unsigned long flags;
194 double value;
195 short eventSeen;
196 char stringValue[256];
197 long usrValue;
198 short changeReferenceLoaded;
199} WAITFOR;
200
201typedef struct
202{
203 char *command;
204 char *emitOnEvent, *emitOnTimeout, *emitOnEnd;
205 FILE *fp;
206} SUBPROCESS;
207
208#define EMIT_EVENT_GIVEN 0x0001U
209#define EMIT_TIMEOUT_GIVEN 0x0002U
210#define EMIT_END_GIVEN 0x0004U
211#define EVENT 0
212#define TIMEOUT 1
213#define LOGIC_ERROR 2
214
215long waitForEvent(double timeLimit, double interval,
216 long invert);
217long rpn_stack_test(long stack_ptr, long numneeded, char *stackname, char *caller);
218long rpn_pop_log(long *logical);
219long rpn_push_log(long logical);
220void rpn_log_and(void);
221void rpn_log_or(void);
222void rpn_log_not(void);
223void rpn_clear_stack();
224void EventHandler(struct event_handler_args event);
225void StringEventHandler(struct event_handler_args event);
226void oag_ca_exception_handler(struct exception_handler_args args);
227long hasStarted = 0;
228
229double pendIOTime;
230WAITFOR *waitFor;
231long waitFors;
232
233#if (EPICS_VERSION > 3)
234long waitForEventPVA(PVA_OVERALL *pva, double timeLimit, double interval, long invert);
235#endif
236
237int main(int argc, char **argv) {
238 long i_arg, j, terminateLoop;
239 unsigned long flags;
240 SCANNED_ARG *s_arg;
241 double interval, timeLimit, totalTimeLimit, startTime;
242 //long useMonitor;
243 long code = 0, repeats, eventEndPending;
244 char *emitOnEvent, *emitOnTimeout, *emitOnEnd;
245 char **preEvent, **postEvent, **onEvent, **onEnd;
246 SUBPROCESS *subprocess;
247 long preEvents, postEvents, onEvents, onEnds, subprocesses;
248 long noWarnings = 0;
249 long providerMode = 0;
250#if (EPICS_VERSION > 3)
251 PVA_OVERALL pva;
252#endif
253
255 argc = scanargs(&s_arg, argc, argv);
256 if (argc < 2)
257 bomb(NULL, USAGE);
258
259 waitFor = NULL;
260 waitFors = 0;
261 interval = 0.1;
262 timeLimit = totalTimeLimit = -1;
263 //useMonitor = 0;
264 repeats = 1;
265 emitOnEvent = emitOnTimeout = emitOnEnd = NULL;
266 preEvent = postEvent = onEvent = onEnd = NULL;
267 subprocess = NULL;
268 preEvents = postEvents = onEvents = onEnds = subprocesses = 0;
269 pendIOTime = 10;
270
271 for (i_arg = 1; i_arg < argc; i_arg++) {
272 if (s_arg[i_arg].arg_type == OPTION) {
273 delete_chars(s_arg[i_arg].list[0], (char *)"_");
274 switch (code = match_string(s_arg[i_arg].list[0], commandline_option, COMMANDLINE_OPTIONS, 0)) {
275 case CLO_INTERVAL:
276 if (s_arg[i_arg].n_items != 2 ||
277 sscanf(s_arg[i_arg].list[1], "%lf", &interval) != 1 ||
278 interval <= 0)
279 SDDS_Bomb((char *)"invalid -interval syntax/value");
280 break;
281 case CLO_TIMELIMIT:
282 if (s_arg[i_arg].n_items != 2 ||
283 sscanf(s_arg[i_arg].list[1], "%lf", &timeLimit) != 1 ||
284 timeLimit <= 0)
285 SDDS_Bomb((char *)"invalid -timeLimit syntax/value");
286 break;
287 case CLO_WAITFOR:
288 if (s_arg[i_arg].n_items < 3)
289 SDDS_Bomb((char *)"invalid -waitFor syntax");
290 waitFor = (WAITFOR *)trealloc(waitFor, sizeof(*waitFor) * (waitFors + 1));
291 waitFor[waitFors].PVname = s_arg[i_arg].list[1];
292 s_arg[i_arg].n_items -= 2;
293 if (!scanItemList(&waitFor[waitFors].flags, s_arg[i_arg].list + 2, &s_arg[i_arg].n_items, 0,
294 "lowerlimit", SDDS_DOUBLE, &waitFor[waitFors].lowerLimit, 1, LOWERLIMIT_GIVEN,
295 "upperlimit", SDDS_DOUBLE, &waitFor[waitFors].upperLimit, 1, UPPERLIMIT_GIVEN,
296 "above", SDDS_DOUBLE, &waitFor[waitFors].above, 1, ABOVE_GIVEN,
297 "below", SDDS_DOUBLE, &waitFor[waitFors].below, 1, BELOW_GIVEN,
298 "equal", SDDS_DOUBLE, &waitFor[waitFors].equalTo, 1, EQUALTO_GIVEN,
299 "sameas", SDDS_STRING, &waitFor[waitFors].sameAs, 1, SAME_AS_GIVEN,
300 "changed", -1, NULL, 0, CHANGED_GIVEN,
301 "monitor", -1, NULL, 0, MONITOR_GIVEN,
302 NULL) ||
303 !waitFor[waitFors].flags ||
304 (waitFor[waitFors].flags & LOWERLIMIT_GIVEN && !(waitFor[waitFors].flags & UPPERLIMIT_GIVEN)) ||
305 (!(waitFor[waitFors].flags & LOWERLIMIT_GIVEN) && waitFor[waitFors].flags & UPPERLIMIT_GIVEN) ||
306 (waitFor[waitFors].flags & LOWERLIMIT_GIVEN &&
307 waitFor[waitFors].lowerLimit >= waitFor[waitFors].upperLimit) ||
308 (waitFor[waitFors].flags & SAME_AS_GIVEN && waitFor[waitFors].flags & (~SAME_AS_GIVEN)))
309 SDDS_Bomb((char *)"invalid -waitFor syntax/values");
310 waitFor[waitFors].operations = 0;
311 waitFor[waitFors].operation = NULL;
312 waitFor[waitFors].changeReferenceLoaded = 0;
313 waitFors++;
314 s_arg[i_arg].n_items += 2;
315 break;
316 case CLO_EZCATIMING:
317 break;
318 case CLO_USEMONITOR:
319 //useMonitor = 1;
320 break;
321 case CLO_NOWARN:
322 noWarnings = 1;
323 break;
324 case CLO_NOT:
325 case CLO_OR:
326 case CLO_AND:
327 if (waitFors < 1 && code == CLO_NOT)
328 SDDS_Bomb((char *)"invalid syntax---give a -waitFor option before -not");
329 if (waitFors < 2 && code != CLO_NOT)
330 SDDS_Bomb((char *)"invalid syntax---give at least two -waitFor options before -or or -and");
331 waitFor[waitFors - 1].operation = (short *)SDDS_Realloc(waitFor[waitFors - 1].operation,
332 sizeof(*waitFor[waitFors - 1].operation) * (waitFor[waitFors - 1].operations + 1));
333 waitFor[waitFors - 1].operation[waitFor[waitFors - 1].operations] = code;
334 waitFor[waitFors - 1].operations++;
335 break;
336 case CLO_REPEAT:
337 if (s_arg[i_arg].n_items == 1)
338 repeats = 0; /* indefinite number of repeats */
339 else if (s_arg[i_arg].n_items != 2 ||
340 sscanf(s_arg[i_arg].list[1], "%ld", &repeats) != 1 ||
341 repeats < 1)
342 SDDS_Bomb((char *)"invalid -repeats syntax/value");
343 break;
344 case CLO_EMIT:
345 s_arg[i_arg].n_items -= 1;
346 if (!scanItemList(&flags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
347 "event", SDDS_STRING, &emitOnEvent, 1, EMIT_EVENT_GIVEN,
348 "timeout", SDDS_STRING, &emitOnTimeout, 1, EMIT_TIMEOUT_GIVEN,
349 "end", SDDS_STRING, &emitOnEnd, 1, EMIT_END_GIVEN,
350 NULL) ||
351 !(flags & EMIT_EVENT_GIVEN))
352 SDDS_Bomb((char *)"invalid -emit syntax/values");
353 s_arg[i_arg].n_items += 1;
354 break;
355 case CLO_PREEVENT:
356 if (s_arg[i_arg].n_items != 2)
357 SDDS_Bomb((char *)"invalid -preEvent syntax");
358 if (!(preEvent = (char **)SDDS_Realloc(preEvent, sizeof(*preEvent) * (preEvents + 1))))
359 SDDS_Bomb((char *)"allocation failure");
360 preEvent[preEvents] = s_arg[i_arg].list[1];
361 preEvents++;
362 break;
363 case CLO_POSTEVENT:
364 if (s_arg[i_arg].n_items != 2)
365 SDDS_Bomb((char *)"invalid -postEvent syntax");
366 if (!(postEvent = (char **)SDDS_Realloc(postEvent, sizeof(*postEvent) * (postEvents + 1))))
367 SDDS_Bomb((char *)"allocation failure");
368 postEvent[postEvents] = s_arg[i_arg].list[1];
369 postEvents++;
370 break;
371 case CLO_SUBPROCESS:
372 if (s_arg[i_arg].n_items < 2)
373 SDDS_Bomb((char *)"invalid -subprocess syntax");
374 if (!(subprocess = (SUBPROCESS *)SDDS_Realloc(subprocess, sizeof(*subprocess) * (subprocesses + 1))))
375 SDDS_Bomb((char *)"allocation failure");
376 subprocess[subprocesses].command = s_arg[i_arg].list[1];
377 s_arg[i_arg].n_items -= 2;
378 if (!scanItemList(&flags, s_arg[i_arg].list + 2, &s_arg[i_arg].n_items, 0,
379 "event", SDDS_STRING, &subprocess[subprocesses].emitOnEvent, 1, EMIT_EVENT_GIVEN,
380 "timeout", SDDS_STRING, &subprocess[subprocesses].emitOnTimeout, 1, EMIT_TIMEOUT_GIVEN,
381 "end", SDDS_STRING, &subprocess[subprocesses].emitOnEnd, 1, EMIT_END_GIVEN,
382 NULL) ||
383 !(flags & EMIT_EVENT_GIVEN))
384 SDDS_Bomb((char *)"invalid -subprocess syntax/values");
385 subprocess[subprocesses].fp = NULL;
386 subprocesses++;
387 s_arg[i_arg].n_items += 2;
388 break;
389 case CLO_ONEVENT:
390 if (s_arg[i_arg].n_items != 2)
391 SDDS_Bomb((char *)"invalid -onEvent syntax");
392 if (!(onEvent = (char **)SDDS_Realloc(onEvent, sizeof(*onEvent) * (onEvents + 1))))
393 SDDS_Bomb((char *)"allocation failure");
394 onEvent[onEvents] = s_arg[i_arg].list[1];
395 onEvents++;
396 break;
397 case CLO_ONEND:
398 if (s_arg[i_arg].n_items != 2)
399 SDDS_Bomb((char *)"invalid -onEnd syntax");
400 if (!(onEnd = (char **)SDDS_Realloc(onEnd, sizeof(*onEnd) * (onEnds + 1))))
401 SDDS_Bomb((char *)"allocation failure");
402 onEnd[onEnds] = s_arg[i_arg].list[1];
403 onEnds++;
404 break;
405 case CLO_TOTALTIMELIMIT:
406 if (s_arg[i_arg].n_items != 2 ||
407 sscanf(s_arg[i_arg].list[1], "%lf", &totalTimeLimit) != 1 ||
408 totalTimeLimit <= 0)
409 SDDS_Bomb((char *)"invalid -totalTimeLimit syntax/value");
410 break;
411 case CLO_PENDIOTIME:
412 if (s_arg[i_arg].n_items != 2)
413 bomb((char *)"wrong number of items for -pendIoTime", NULL);
414 if (sscanf(s_arg[i_arg].list[1], "%lf", &pendIOTime) != 1 ||
415 pendIOTime <= 0)
416 bomb((char *)"invalid -pendIoTime value (cawait)", NULL);
417 break;
418 case CLO_PROVIDER:
419 if (s_arg[i_arg].n_items != 2)
420 SDDS_Bomb((char *)"no value given for option -provider");
421 if ((providerMode = match_string(s_arg[i_arg].list[1], providerOption,
422 PROVIDER_COUNT, 0)) < 0)
423 SDDS_Bomb((char *)"invalid -provider");
424 break;
425 default:
426 SDDS_Bomb((char *)"unknown option");
427 break;
428 }
429 } else
430 SDDS_Bomb((char *)"unknown option");
431 }
432
433 if (!waitFors)
434 SDDS_Bomb((char *)"-waitFor option must be given at least once");
435 if (providerMode == PROVIDER_PVA) {
436#if (EPICS_VERSION > 3)
437 //Allocate memory for pva structure
438 allocPVA(&pva, waitFors, 0);
439 //List PV names
440 epics::pvData::shared_vector<std::string> names(pva.numPVs);
441 epics::pvData::shared_vector<std::string> provider(pva.numPVs);
442 for (j = 0; j < pva.numPVs; j++) {
443 if (strncmp(waitFor[j].PVname, "pva://", 6) == 0) {
444 waitFor[j].PVname += 6;
445 names[j] = waitFor[j].PVname;
446 waitFor[j].PVname -= 6;
447 provider[j] = "pva";
448 } else if (strncmp(waitFor[j].PVname, "ca://", 5) == 0) {
449 waitFor[j].PVname += 5;
450 names[j] = waitFor[j].PVname;
451 waitFor[j].PVname -= 5;
452 provider[j] = "ca";
453 } else {
454 names[j] = waitFor[j].PVname;
455 provider[j] = "pva";
456 }
457 }
458 pva.pvaChannelNames = freeze(names);
459 pva.pvaProvider = freeze(provider);
460 //Connect to PVs
461 ConnectPVA(&pva, pendIOTime);
462
463 for (j = 0; j < pva.numPVs; j++) {
464 if (!pva.isConnected[j]) {
465 fprintf(stderr, "Error (cawait): problem doing search for %s\n", waitFor[j].PVname);
466 return (EXIT_FAILURE);
467 }
468 }
469 //Get initial values
470 if (GetPVAValues(&pva) == 1) {
471 return (EXIT_FAILURE);
472 }
473 if (MonitorPVAValues(&pva) == 1) {
474 return (EXIT_FAILURE);
475 }
476 epicsThreadSleep(.1);
477 if (PollMonitoredPVA(&pva)) {
478 //fprintf(stderr, "something changed\n");
479 }
480#else
481 fprintf(stderr, "-provider=pva is only available with newer versions of EPICS\n");
482 return (EXIT_FAILURE);
483#endif
484 } else {
485 if (ca_task_initialize() != ECA_NORMAL) {
486 fprintf(stderr, "Error (cawait): problem initializing channel access\n");
487 return (EXIT_FAILURE);
488 }
489 ca_add_exception_event(oag_ca_exception_handler, NULL);
490
491 /* connect to all PVs */
492 for (j = 0; j < waitFors; j++) {
493 if (ca_search(waitFor[j].PVname, &waitFor[j].channelID) != ECA_NORMAL) {
494 fprintf(stderr, "Error (cawait): problem doing search for %s\n",
495 waitFor[j].PVname);
496 ca_task_exit();
497 return (EXIT_FAILURE);
498 }
499 waitFor[j].usrValue = j;
500 }
501
502 ca_pend_io(pendIOTime);
503
504#ifdef DEBUG
505 fprintf(stderr, "Returned from ca_pend_io after ca_search calls\n");
506#endif
507
508 for (j = 0; j < waitFors; j++) {
509 if (ca_state(waitFor[j].channelID) != cs_conn) {
510 fprintf(stderr, "Error (cawait): no connection in time for %s\n",
511 waitFor[j].PVname);
512 ca_task_exit();
513 return (EXIT_FAILURE);
514 }
515 waitFor[j].eventSeen = -1;
516 if (!(waitFor[j].flags & SAME_AS_GIVEN)) {
517 waitFor[j].channelType = DBF_DOUBLE;
518 if (ca_add_masked_array_event(DBR_TIME_DOUBLE, 1, waitFor[j].channelID,
519 EventHandler, (void *)&waitFor[j].usrValue,
520 (ca_real)0, (ca_real)0, (ca_real)0,
521 NULL, DBE_VALUE) != ECA_NORMAL) {
522 fprintf(stderr, "error: unable to setup callback for PV %s\n", waitFor[j].PVname);
523 ca_task_exit();
524 return (EXIT_FAILURE);
525 }
526 } else {
527 waitFor[j].channelType = DBF_STRING;
528 if (ca_add_masked_array_event(DBR_TIME_STRING, 1, waitFor[j].channelID,
529 StringEventHandler, (void *)&waitFor[j].usrValue,
530 (ca_real)0, (ca_real)0, (ca_real)0,
531 NULL, DBE_VALUE) != ECA_NORMAL) {
532 fprintf(stderr, "error: unable to setup callback for PV %s\n", waitFor[j].PVname);
533 ca_task_exit();
534 return (EXIT_FAILURE);
535 }
536 }
537 }
538 if (ca_pend_io(pendIOTime) != ECA_NORMAL) {
539 fprintf(stderr, "Error (cawait): unable to setup callbacks for PVs\n");
540 ca_task_exit();
541 return (EXIT_FAILURE);
542 }
543#ifdef DEBUG
544 fprintf(stderr, "ca_pend_io called after ca_add_masked_array_event calls\n");
545#endif
546 }
547
548 /* initialize the subprocesses */
549 for (j = 0; j < subprocesses; j++) {
550 if (!(subprocess[j].fp = popen(subprocess[j].command, "w"))) {
551 fprintf(stderr, "Error (cawait): problem starting subprocess with command %s\n", subprocess[j].command);
552 if (providerMode != PROVIDER_PVA)
553 ca_task_exit();
554 return (EXIT_FAILURE);
555 }
556 }
557
558 startTime = getTimeInSecs();
559 if (totalTimeLimit > 0 && totalTimeLimit < timeLimit) {
560 timeLimit = totalTimeLimit;
561 }
562 eventEndPending = 0; /* indicates that the end of the last event is pending. */
563 do {
564#ifdef DEBUG
565 fprintf(stderr, "At top of main loop\n");
566#endif
567 terminateLoop = 0;
568 if (totalTimeLimit > 0 && ((getTimeInSecs() - startTime) > totalTimeLimit)) {
569 terminateLoop = 1;
570 }
571
572 if (!eventEndPending) {
573 if (!terminateLoop) {
574 /* execute pre-event commands */
575 for (j = 0; j < preEvents; j++) {
576 system(preEvent[j]);
577 }
578
579 if (providerMode == PROVIDER_PVA) {
580#if (EPICS_VERSION > 3)
581 /* wait for event to become true */
582 code = waitForEventPVA(&pva, timeLimit, interval, 0);
583#endif
584 } else {
585 code = waitForEvent(timeLimit, interval, 0);
586 }
587
588 if (code == LOGIC_ERROR) {
589 fputs("logic error---probably too few operators (cawait)\n", stderr);
590 if (providerMode != PROVIDER_PVA)
591 ca_task_exit();
592 return (EXIT_FAILURE);
593 }
594
595 /* emit messages to standard output, as requested */
596 if (emitOnEvent) {
597 switch (code) {
598 case EVENT:
599 puts(emitOnEvent);
600 fflush(stdout);
601 break;
602 case TIMEOUT:
603 if (emitOnTimeout) {
604 puts(emitOnTimeout);
605 fflush(stdout);
606 }
607 break;
608 default:
609 break;
610 }
611 }
612
613 /* emit messages to subprocesses, as requested */
614 for (j = 0; j < subprocesses; j++) {
615 switch (code) {
616 case EVENT:
617 fputs(subprocess[j].emitOnEvent, subprocess[j].fp);
618 fputc('\n', subprocess[j].fp);
619 fflush(subprocess[j].fp);
620 break;
621 case TIMEOUT:
622 if (subprocess[j].emitOnTimeout) {
623 fputs(subprocess[j].emitOnTimeout, subprocess[j].fp);
624 fputc('\n', subprocess[j].fp);
625 fflush(subprocess[j].fp);
626 }
627 break;
628 }
629 }
630
631 /* execute on-event commands */
632 if (code == EVENT) {
633 for (j = 0; j < onEvents; j++) {
634 system(onEvent[j]);
635 }
636 }
637 }
638 }
639
640 if (repeats == 1 || terminateLoop) {
641 if (!eventEndPending) {
642 /* last repeat */
643 if (emitOnEnd) {
644 /* emit end-of-run message to stdout */
645 puts(emitOnEnd);
646 fflush(stdout);
647 }
648 /* execute end-of-run commands */
649 for (j = 0; j < onEnds; j++) {
650 system(onEnd[j]);
651 }
652 /* emit end-of-run messages to subprocesses */
653 for (j = 0; j < subprocesses; j++) {
654 if (subprocess[j].emitOnEnd) {
655 fputs(subprocess[j].emitOnEnd, subprocess[j].fp);
656 fputc('\n', subprocess[j].fp);
657 fflush(subprocess[j].fp);
658 }
659 }
660#if (EPICS_VERSION > 3)
661 if (providerMode == PROVIDER_PVA)
662 freePVA(&pva);
663#endif
664 free_scanargs(&s_arg, argc);
665 if (terminateLoop || eventEndPending) {
666 if (providerMode != PROVIDER_PVA)
667 ca_task_exit();
668 return (EXIT_SUCCESS);
669 }
670 /* exit with error if non-event end */
671 return (code == EVENT ? 0 : 1);
672 }
673 }
674
675#ifdef DEBUG
676 fprintf(stderr, "Waiting for event\n");
677#endif
678 if (providerMode == PROVIDER_PVA) {
679#if (EPICS_VERSION > 3)
680 /* wait for event to become false */
681 code = waitForEventPVA(&pva, timeLimit, interval, 1);
682#endif
683 } else {
684 code = waitForEvent(timeLimit, interval, 1);
685 }
686 if (code == TIMEOUT) {
687 eventEndPending = 1;
688 if (!noWarnings) {
689 fprintf(stderr, "warning: timeout error waiting for event to clear (cawait)\n");
690 }
691 } else {
692 eventEndPending = 0;
693 /* execute post-event commands */
694 for (j = 0; j < postEvents; j++) {
695 system(postEvent[j]);
696 }
697 }
698 for (j = 0; j < waitFors; j++) {
699 waitFor[j].changeReferenceLoaded = 0;
700 }
701 } while (--repeats);
702
703 if (providerMode != PROVIDER_PVA)
704 ca_task_exit();
705
706 SDDS_Bomb((char *)"Abnormal exit from repeats loop--this shouldn't happen");
707 return (EXIT_FAILURE);
708}
709
710void EventHandler(struct event_handler_args event) {
711 struct dbr_time_double *dbrValue;
712 long index;
713 index = *((long *)event.usr);
714 dbrValue = (struct dbr_time_double *)event.dbr;
715 waitFor[index].value = (double)dbrValue->value;
716 if (waitFor[index].eventSeen == -1)
717 waitFor[index].eventSeen = 0;
718 else
719 waitFor[index].eventSeen = 1;
720}
721
722void StringEventHandler(struct event_handler_args event) {
723 struct dbr_time_string *dbrValue;
724 long index;
725 index = *((long *)event.usr);
726 dbrValue = (struct dbr_time_string *)event.dbr;
727 sprintf(waitFor[index].stringValue, "%s", (char *)dbrValue->value);
728 if (waitFor[index].eventSeen == -1)
729 waitFor[index].eventSeen = 0;
730 else
731 waitFor[index].eventSeen = 1;
732}
733
734void sleep_us(unsigned int nusecs) {
735 struct timeval tval;
736
737 tval.tv_sec = nusecs / 1000000;
738 tval.tv_usec = nusecs % 1000000;
739 select(0, NULL, NULL, NULL, &tval);
740}
741
742#define RPN_LOGICSTACKSIZE 500
743
744/* stack for logical operations */
745short rpn_logicstack[RPN_LOGICSTACKSIZE];
746long rpn_lstackptr = 0;
747
748long waitForEvent(double timeLimit, double interval,
749 long invert) {
750 double time0, timeNow;
751 long j, k, result, term;
752
753 time0 = getTimeInSecs();
754 if (timeLimit > 0)
755 timeLimit += time0;
756 timeNow = getTimeInSecs();
757 while (timeLimit < 0 || timeNow < timeLimit) {
758#ifdef DEBUG
759 fprintf(stderr, "At top of while loop in waitForEvent()\n");
760#endif
761 if (!hasStarted) {
762 for (j = 0; j < waitFors; j++) {
763 /* request data for all PVs */
764 if (ca_get(waitFor[j].channelType, waitFor[j].channelID,
765 (waitFor[j].channelType == DBF_DOUBLE ? (void *)&waitFor[j].value : (void *)&waitFor[j].stringValue)) != ECA_NORMAL) {
766 fprintf(stderr, "Error (cawait): problem doing ca_get for %s\n",
767 waitFor[j].PVname);
768 ca_task_exit();
769 exit(EXIT_FAILURE);
770 }
771 }
772 if (ca_pend_io(pendIOTime) != ECA_NORMAL) {
773 fprintf(stderr, "Error (cawait): problem doing ca_get for PVs\n");
774 ca_task_exit();
775 exit(EXIT_FAILURE);
776 }
777 hasStarted = 1;
778#ifdef DEBUG
779 for (j = 0; j < waitFors; j++) {
780 switch (waitFor[j].channelType) {
781 case DBF_DOUBLE:
782 fprintf(stderr, "Initial %s = %e\n", waitFor[j].PVname, waitFor[j].value);
783 break;
784 default:
785 fprintf(stderr, "Initial %s = %s\n", waitFor[j].PVname, waitFor[j].stringValue);
786 break;
787 }
788 }
789#endif
790 }
791 for (j = 0; j < waitFors; j++) {
792 if (waitFor[j].flags & CHANGED_GIVEN && !waitFor[j].changeReferenceLoaded) {
793 waitFor[j].changeReference = waitFor[j].value;
794 waitFor[j].changeReferenceLoaded = 1;
795 }
796 }
797 rpn_clear_stack();
798 for (j = 0; j < waitFors; j++) {
799 term = 1;
800 if (waitFor[j].flags & SAME_AS_GIVEN) {
801 term = !strcmp(waitFor[j].stringValue, waitFor[j].sameAs);
802 } else if (waitFor[j].flags & EQUALTO_GIVEN) {
803 if (waitFor[j].value != waitFor[j].equalTo)
804 term = 0;
805 } else if (waitFor[j].flags & (LOWERLIMIT_GIVEN + UPPERLIMIT_GIVEN)) {
806 if (waitFor[j].value < waitFor[j].lowerLimit ||
807 waitFor[j].value > waitFor[j].upperLimit)
808 term = 0;
809 } else if (waitFor[j].flags & ABOVE_GIVEN) {
810 if (waitFor[j].value <= waitFor[j].above)
811 term = 0;
812 } else if (waitFor[j].flags & BELOW_GIVEN) {
813 if (waitFor[j].value >= waitFor[j].below)
814 term = 0;
815 } else if (waitFor[j].flags & CHANGED_GIVEN) {
816 if (waitFor[j].value == waitFor[j].changeReference)
817 term = 0;
818 else
819 waitFor[j].changeReference = waitFor[j].value;
820 } else if (waitFor[j].flags & MONITOR_GIVEN) {
821 if (waitFor[j].eventSeen == 1) {
822 waitFor[j].eventSeen = 0;
823 } else {
824 term = 0;
825 }
826 } else {
827 fprintf(stderr, "no flags set for PV %s--this shouldn't happen\n",
828 waitFor[j].PVname);
829 ca_task_exit();
830 exit(EXIT_FAILURE);
831 }
832 rpn_push_log(term);
833 for (k = 0; k < waitFor[j].operations; k++) {
834 switch (waitFor[j].operation[k]) {
835 case OP_NOT:
836 rpn_log_not();
837 break;
838 case OP_OR:
839 rpn_log_or();
840 break;
841 case OP_AND:
842 rpn_log_and();
843 break;
844 default:
845 ca_task_exit();
846 SDDS_Bomb((char *)"invalid operation seen---this shouldn't happen");
847 break;
848 }
849 }
850 }
851 rpn_pop_log(&result);
852 if (rpn_lstackptr != 0)
853 return LOGIC_ERROR;
854 if (invert)
855 result = !result;
856 if (result)
857 return EVENT;
858#ifdef DEBUG
859 fprintf(stderr, "At bottom of while loop in waitForEvent(). Calling ca_pend_event().\n");
860#endif
861 ca_pend_event(interval);
862 timeNow = getTimeInSecs();
863 }
864 return TIMEOUT;
865}
866
867#if (EPICS_VERSION > 3)
868long waitForEventPVA(PVA_OVERALL *pva, double timeLimit, double interval, long invert) {
869 double time0, timeNow;
870 long j, k, result, term;
871 time0 = getTimeInSecs();
872 if (timeLimit > 0)
873 timeLimit += time0;
874 timeNow = getTimeInSecs();
875 while (timeLimit < 0 || timeNow < timeLimit) {
876 if (!hasStarted) {
877 hasStarted = 1;
878 }
879 for (j = 0; j < waitFors; j++) {
880 if ((pva->isConnected[j]) && (pva->pvaData[j].fieldType == epics::pvData::scalar)) {
881 if (waitFor[j].flags & CHANGED_GIVEN && !waitFor[j].changeReferenceLoaded) {
882 waitFor[j].changeReference = pva->pvaData[j].getData[0].values[0];
883 waitFor[j].changeReferenceLoaded = 1;
884 }
885 }
886 }
887 rpn_clear_stack();
888 term = -1;
889 for (j = 0; j < waitFors; j++) {
890 if ((pva->isConnected[j]) && (pva->pvaData[j].numMonitorReadings > 0)) {
891 if ((pva->pvaData[j].fieldType == epics::pvData::scalar)) {
892 term = 1;
893 if (waitFor[j].flags & SAME_AS_GIVEN) {
894 term = !strcmp(pva->pvaData[j].monitorData[0].stringValues[0], waitFor[j].sameAs);
895 } else if (waitFor[j].flags & EQUALTO_GIVEN) {
896 if (pva->pvaData[j].monitorData[0].values[0] != waitFor[j].equalTo)
897 term = 0;
898 } else if (waitFor[j].flags & (LOWERLIMIT_GIVEN + UPPERLIMIT_GIVEN)) {
899 if (pva->pvaData[j].monitorData[0].values[0] < waitFor[j].lowerLimit ||
900 pva->pvaData[j].monitorData[0].values[0] > waitFor[j].upperLimit)
901 term = 0;
902 } else if (waitFor[j].flags & ABOVE_GIVEN) {
903 if (pva->pvaData[j].monitorData[0].values[0] <= waitFor[j].above)
904 term = 0;
905 } else if (waitFor[j].flags & BELOW_GIVEN) {
906 if (pva->pvaData[j].monitorData[0].values[0] >= waitFor[j].below)
907 term = 0;
908 } else if (waitFor[j].flags & CHANGED_GIVEN) {
909 if (pva->pvaData[j].monitorData[0].values[0] == waitFor[j].changeReference)
910 term = 0;
911 else
912 waitFor[j].changeReference = pva->pvaData[j].monitorData[0].values[0];
913 } else {
914 fprintf(stderr, "no flags set for PV %s--this shouldn't happen\n", waitFor[j].PVname);
915 exit(EXIT_FAILURE);
916 }
917 } else { //For scalarArray PVs, it may be useful to wait on the .timeStamp.secondsPast
918 fprintf(stderr, "error: cawait only works with scalar values.\n");
919 exit(EXIT_FAILURE);
920 }
921 rpn_push_log(term);
922 for (k = 0; k < waitFor[j].operations; k++) {
923 switch (waitFor[j].operation[k]) {
924 case OP_NOT:
925 rpn_log_not();
926 break;
927 case OP_OR:
928 rpn_log_or();
929 break;
930 case OP_AND:
931 rpn_log_and();
932 break;
933 default:
934 SDDS_Bomb((char *)"invalid operation seen---this shouldn't happen");
935 break;
936 }
937 }
938 }
939 }
940 if (term != -1) {
941 rpn_pop_log(&result);
942 if (rpn_lstackptr != 0)
943 return LOGIC_ERROR;
944 if (invert)
945 result = !result;
946 if (result)
947 return EVENT;
948 }
949 epicsThreadSleep(interval);
950 if (PollMonitoredPVA(pva)) {
951 //fprintf(stderr, "something changed\n");
952 }
953 timeNow = getTimeInSecs();
954 }
955 return TIMEOUT;
956}
957#endif
958
959void rpn_clear_stack() {
960 rpn_lstackptr = 0;
961}
962
963long rpn_stack_test(long stack_ptr, long numneeded, char *stackname, char *caller) {
964 if (stack_ptr < numneeded) {
965 fprintf(stderr, "too few items on %s stack (%s)\n", stackname, caller);
966 return 0;
967 }
968 return (1);
969}
970
971long rpn_pop_log(long *logical) {
972 if (rpn_lstackptr < 1) {
973 fputs("too few items on logical stack (rpn_pop_log)\n", stderr);
974 ca_task_exit();
975 exit(EXIT_FAILURE);
976 }
977 *logical = rpn_logicstack[--rpn_lstackptr];
978 return (1);
979}
980
981long rpn_push_log(long logical) {
982 if (rpn_lstackptr == RPN_LOGICSTACKSIZE) {
983 fputs("stack overflow--logical stack size exceeded (rpn_push_log)\n", stderr);
984 ca_task_exit();
985 exit(EXIT_FAILURE);
986 }
987 rpn_logicstack[rpn_lstackptr++] = logical;
988 return (1);
989}
990
991void rpn_log_and(void) {
992 if (!rpn_stack_test(rpn_lstackptr, 2, (char *)"logical", (char *)"rpn_log_and")) {
993 ca_task_exit();
994 exit(EXIT_FAILURE);
995 }
996 rpn_logicstack[rpn_lstackptr - 2] = (rpn_logicstack[rpn_lstackptr - 1] && rpn_logicstack[rpn_lstackptr - 2]);
997 rpn_lstackptr--;
998}
999
1000void rpn_log_or(void) {
1001 if (!rpn_stack_test(rpn_lstackptr, 2, (char *)"logical", (char *)"rpn_log_or")) {
1002 ca_task_exit();
1003 exit(EXIT_FAILURE);
1004 }
1005 rpn_logicstack[rpn_lstackptr - 2] = (rpn_logicstack[rpn_lstackptr - 1] || rpn_logicstack[rpn_lstackptr - 2]);
1006 rpn_lstackptr--;
1007}
1008
1009void rpn_log_not(void) {
1010 if (!rpn_stack_test(rpn_lstackptr, 1, (char *)"logical", (char *)"rpn_log_not")) {
1011 ca_task_exit();
1012 exit(EXIT_FAILURE);
1013 }
1014 rpn_logicstack[rpn_lstackptr - 1] = !rpn_logicstack[rpn_lstackptr - 1];
1015}
1016
1017void oag_ca_exception_handler(struct exception_handler_args args) {
1018 char *pName;
1019 int severityInt;
1020 static const char *severity[] =
1021 {
1022 "Warning",
1023 "Success",
1024 "Error",
1025 "Info",
1026 "Fatal",
1027 "Fatal",
1028 "Fatal",
1029 "Fatal"};
1030
1031 severityInt = CA_EXTRACT_SEVERITY(args.stat);
1032
1033 if ((severityInt != 1) && (severityInt != 3)) {
1034 fprintf(stderr, "CA.Client.Exception................\n");
1035 fprintf(stderr, " %s: \"%s\"\n",
1036 severity[severityInt],
1037 ca_message(args.stat));
1038
1039 if (args.ctx) {
1040 fprintf(stderr, " Context: \"%s\"\n", args.ctx);
1041 }
1042 if (args.chid) {
1043 pName = (char *)ca_name(args.chid);
1044 fprintf(stderr, " Channel: \"%s\"\n", pName);
1045 fprintf(stderr, " Type: \"%s\"\n", dbr_type_to_text(args.type));
1046 }
1047 fprintf(stderr, "This sometimes indicates an IOC that is hung up.\n");
1048 _exit(EXIT_FAILURE);
1049 } else {
1050 fprintf(stdout, "CA.Client.Exception................\n");
1051 fprintf(stdout, " %s: \"%s\"\n",
1052 severity[severityInt],
1053 ca_message(args.stat));
1054
1055 if (args.ctx) {
1056 fprintf(stdout, " Context: \"%s\"\n", args.ctx);
1057 }
1058 if (args.chid) {
1059 pName = (char *)ca_name(args.chid);
1060 fprintf(stdout, " Channel: \"%s\"\n", pName);
1061 fprintf(stdout, " Type: \"%s\"\n", dbr_type_to_text(args.type));
1062 }
1063 }
1064}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:288
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
Definition SDDS_utils.c:342
void * SDDS_Realloc(void *old_ptr, size_t new_size)
Reallocates memory to a new size.
Definition SDDS_utils.c:677
#define SDDS_STRING
Identifier for the string data type.
Definition SDDStypes.h:85
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
Definition array.c:181
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
Definition bomb.c:26
char * delete_chars(char *s, char *t)
Removes all occurrences of characters found in string t from string s.
double getTimeInSecs()
Get the current time in seconds since the Epoch with high resolution.
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.
Functions for managing and interacting with Process Variable Array (PVA) structures.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
Definition scanargs.c:36
void free_scanargs(SCANNED_ARG **scanned, int argc)
Definition scanargs.c:584
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.