/* following needed when using EZCA */ #include <tsDefs.h> #include <cadef.h> #include <ezca.h> main() { double d; short s[2]; s[0] = 10; s[1] = -21; ezcaPut("mywaveform", ezcaShort, 2, s); ezcaGet("mygenerator", ezcaDouble, 1, &d); } /* end main() */First note the three include files necessary for all EZCA programs. The first two, tsDefs.h and cadef.h, are EPICS include files. The last, ezca.h, is a new EPICS include file. The files must appear in the same order as they appear above.
Our first call is to ezcaPut. The first argument specifies the process variable we wish to write to, mywaveform. The next two arguments state that we are supplying the data as C short variables and that there are 2 of them. The last argument is a pointer to the data.
Our last call is to ezcaGet. We are retrieving a scalar from the process variable mygenerator in the form of a C double and are placing it into the variable d.
CC = acc EPICSDIR = /home/opiinj2/epics EPICS_ADD_ON_DIR = $(EPICSDIR)/add_on EZCAINCDIR = -I$(EPICS_ADD_ON_DIR)/include EPICSINCDIR = -I$(EPICSDIR)/R3.11.6/share/epicsH INCLUDEDIRS = $(EZCAINCDIR) $(EPICSINCDIR) EZCALIBDIR = $(EPICS_ADD_ON)/lib EZCALIB = -lezca CALIBDIR = $(EPICSDIR)/R3.11.6/Unix/sun4/bin CALIBS = -lca -lCom -lUnix LIBDIRS = -L$(CALIBDIR) -L$(EZCALIBDIR) LIBS = $(EZCALIB) $(CALIBS) CFLAGS = -c DEFINES = -DUNIX -USUN4 -DOLDCA usr1: usr1.o $(CC) -o usr1 usr1.o $(LIBDIRS) $(LIBS) clean: /bin/rm -f *.o usr1 .c.o: $*.c $(CC) $(CFLAGS) $(INCLUDEDIRS) $(DEFINES) $*.c
In this section we introduce the main work functions in EZCA, the functions that read/write data. There are 8 functions that read data all starting with ezcaGet and one function to write data, ezcaPut.
TS_STAMP is an EPICS type (found in tsDefs.h). It reflects the last time the record was processed. There is an EPICS library to manipulate this data type.
In ezcaGetUnits the character array units must be at least EZCA_UNITS_SIZE (defined in ezca.h) big.
ezcaPerror prints the optional prefix with the error message to stdout. ezcaGetErrorString allocates a buffer and fills it with the optional prefix and error message. It then returns the address of the buffer in the user-supplied pointer buff. It then becomes the user's responsibility to free the buffer using the following.
#include <stdio.h> /* following needed when using EZCA */ #include <tsDefs.h> #include <cadef.h> #include <ezca.h> main() { double d; short s[2]; char *error_msg_buff; ezcaAutoErrorMessageOff(); s[0] = 10; s[1] = -21; if (ezcaPut("mywaveform", ezcaShort, 2, s)) ezcaPerror("Put Error:"); if (ezcaGet("mygenerator", ezcaDouble, 1, &d)) { ezcaGetErrorString(NULL, &error_msg_buff); printf("Get Error: %s\n", error_msg_buff); ezcaFree((void *) error_msg_buff); } /* endif */ } /* end main() */The first thing to notice is the new include file stdio.h. It was brought in because we used the constant NULL in our call to ezcaGetErrorString.
We introduced a new variable, error_msg_buff, where we plan to have ezcaGetErrorString place the address of the buffer it allocates.
Our first call is to ezcaAutoErrorMessageOff. This turns off the automatic error message reporting that is the default in EZCA. We could have left it on. Doing so in this program would have resulted in errors being reported twice, once automatically and once with our explicit requests.
Our call to ezcaPut is the same. This time we test the return status. Recall a return status of 0 is always an indication of success. Anything else indicates a problem. Here we use ezcaPerror to report the problem with the write using the prefix ``Put Error:''. If something went wrong with the write, the error message would be written to stdout with our specified prefix.
Our call to ezcaGet is also the same. Here we used ezcaGetErrorString without specifying the optional prefix to have the error message placed into an allocated buffer and the address of the buffer placed into our variable error_msg_buff. We print the error with our own prefix message specified in the print statement rather than the EZCA function and then free the allocated string using ezcaFree.
Assuming that both process variables cannot be found on any IOCs, executing usr2 would look like this.
% usr2 Put Error: ezcaPut(): channel not currently connected Get Error: ezcaGet(): channel not currently connected %
Not all of the EZCA functions respect the context of a group. Only those functions mentioned in section 3 have their work postponed. All other EZCA functions are always performed immediately.
Consider the following program usr3.c which is another modification of usr1.c.
#include <stdio.h> /* following needed when using EZCA */ #include <tsDefs.h> #include <cadef.h> #include <ezca.h> main() { double d; short s[2]; char *error_msg_buff; ezcaAutoErrorMessageOff(); ezcaStartGroup(); s[0] = 10; s[1] = -21; ezcaPut("mywaveform", ezcaShort, 2, s); ezcaGet("mygenerator", ezcaDouble, 1, &d); if (ezcaEndGroup()) ezcaPerror(NULL); } /* end main() */Note that we have removed the error reporting functions found in usr2.c from ezcaPut and ezcaGet, although we were not required to do so. We have also placed both of these functions in a group by surrounding them with ezcaStartGroup and ezcaEndGroup.
By placing these functions into a group we have postponed the work until ezcaEndGroup is encountered. Each function simply checks the validity of the arguments and places the work onto a list to be processed later. ezcaEndGroup performs all the batched work. It returns the first encountered non-successful return code (based on their order of appearance) in the group. If all the batched work returned successfully, ezcaEndGroup returns EZCA_OK which is 0.
ezcaPerror behaves a little bit differently here. When called after ezcaEndGroup (and before a call to any other EZCA function) it prints a status line for every function in the group. This includes those functions with successful return codes.
Under the same assumption as before, that none of the process variables can be found, executing usr3 would look like this.
% usr3 ezcaPut(): channel not currently connected ezcaGet(): channel not currently connected %Assuming that all the arguments to all the functions in the group are valid, there is no difference in placing ezcaStartGroup and ezcaEndGroup around the EZCA functions as they appear in usr1.c or in usr2.c. In usr2.c the return code from each EZCA function would have indicated success (assuming all the arguments are valid).
If some of the arguments were invalid, then surrounding the EZCA calls with ezcaStartGroup and ezcaEndGroup in usr1.c and usr2.c would simply produce different output. In usr2.c the invalid argument error message would appear twice, once immediately after the function call and once as a result of the ezcaPerror at the end of the group.
Users interested in the return status of all the all the EZCA calls in a group should use ezcaEndGroupWithReport instead of ezcaEndGroup.
Like ezcaEndGroup, ezcaEndGroupWithReport performs all the batched work and returns the first encountered non-successful return code or EZCA_OK. Additionally, ezcaEndGroupWithReport allocates a vector of return codes, one element for each member of the group, and returns that vector as well as its length to the user. It then becomes the user's responsibility to free the acquired vector using ezcaFree.
Consider the following program usr4.c, a modification of usr3.c where we replaced ezcaEndGroup with ezcaEndGroupWithReport.
#include <stdio.h> /* following needed when using EZCA */ #include <tsDefs.h> #include <cadef.h> #include <ezca.h> main() { double d; short s[2]; char *error_msg_buff; int i, *rcs, nrcs; ezcaAutoErrorMessageOff(); ezcaStartGroup(); s[0] = 10; s[1] = -21; ezcaPut("mywaveform", ezcaShort, 2, s); ezcaGet("mygenerator", ezcaDouble, 1, &d); ezcaEndGroupWithReport(&rcs, &nrcs); for (i = 0; i < nrcs; i ++) if (rcs[i] != EZCA_OK) printf("Call %d had abnormal return status %d\n", i, rcs[i]); ezcaFree((void *) rcs); } /* end main() */
#include <stdio.h> /* following needed when using EZCA */ #include <tsDefs.h> #include <cadef.h> #include <ezca.h> main() { double d; short s[2]; ezcaAutoErrorMessageOff(); ezcaStartGroup(); ezcaGet("mygenerator", ezcaDouble, 1, &d); if (d < 0) { s[0] = 10; s[1] = -21; ezcaPut("mywaveform", ezcaShort, 2, s); } /* endif */ if (ezcaEndGroup()) ezcaPerror(NULL); } /* end main() */Here we have changed the order of the read and write. We read first, and based on the value we read we conditionally write. This program is destined to fail. Recall that the ezcaGet read is postponed until ezcaEndGroup is executed. This means that the value found in the variable d is garbage when the test on it is performed.
Monitors can be placed and removed at any time using the following.
To alleviate this problem, EZCA provides a function that should be called whenever using monitors and there is a substantial amount of time between any two adjacent EZCA calls. Between all such pair of calls, the user should call the following function.
The default values for these parameters strikes a balance that hopefully serves most users efficiently. The hope is that those users that can connect quickly are served as well as those that require a little more time.
Users can not only discover the values of these parameters using
Empirically, we have observed that under normal circumstances EZCA can reliably process (read or write) 200 process variables per second. Users should adjust timeout and retrycount accordingly.
For example, with a timeout of 0.2 seconds and a retrycount of 9, EZCA will wait a maximum of 0.2*(1+9) = 2 seconds. This will allow the user to process groups of up to 400 gets and/or puts reliably. If a particular group has 600 operations to perform, the user must increase the maximum timeout to at least 3 seconds to more adequately assure reliable processing. This may be done by increasing timeout and/or retrycount accordingly.
Prior to release 3.11.6, writing in CA simply initiated the write and there was no way to automatically validate that the value got to the process variable or that all resulting processing was successfully completed (ca_array_put).
Users performing ezcaPut to process variables residing on IOCs booted with versions of EPICS prior to 3.11.6 will always get return codes indicating timeout errors for all such writes. The antiquated versions of EPICS never acknowledge the write, so EZCA is fooled into thinking that the write was not successful.
In order to eliminate these annoying error messages, we have provided a backward compatible version of EZCA, one that uses older ca_array_put instead of the newer ca_array_put_callback. To use this version the user simply adds one switch to the compile line of the makefile. No change is necessary to the user code. Below is a copy of the makefile presented in section 2.2. It already has the compile switch in place.
CC = acc EPICSDIR = /home/opiinj2/epics EPICS_ADD_ON_DIR = $(EPICSDIR)/add_on EZCAINCDIR = -I$(EPICS_ADD_ON_DIR)/include EPICSINCDIR = -I$(EPICSDIR)/R3.11.6/share/epicsH INCLUDEDIRS = $(EZCAINCDIR) $(EPICSINCDIR) EZCALIBDIR = $(EPICS_ADD_ON)/lib EZCALIB = -lezca CALIBDIR = $(EPICSDIR)/R3.11.6/Unix/sun4/bin CALIBS = -lca -lCom -lUnix LIBDIRS = -L$(CALIBDIR) -L$(EZCALIBDIR) LIBS = $(EZCALIB) $(CALIBS) CFLAGS = -c DEFINES = -DUNIX -USUN4 -DOLDCA usr1: usr1.o $(CC) -o usr1 usr1.o $(LIBDIRS) $(LIBS) clean: /bin/rm -f *.o usr1 .c.o: $*.c $(CC) $(CFLAGS) $(INCLUDEDIRS) $(DEFINES) $*.cThis changes all writes to use the old CA write. It does not force the old version to acknowledge the write, it simply instructs EZCA to use the old CA write (ca_array_put) and not to wait for the acknowledgment.
To take advantage of the new CA write (ca_array_put_callback), simply remove -DOLDCA from the makefile and re-compile.