Files
caClientLessons/caLesson3/caLesson3.c

339 lines
12 KiB
C

/* caLesson3.c
by Dirk Zimoch, 2007
Meanwhile, we know how to read double values from EPICS.
Now we will learn about other data types. Some include
more information than others.
And we will read in a loop.
*/
#include <stdio.h>
/* include EPICS headers */
#include <cadef.h>
#define epicsAlarmGLOBAL
#include <alarm.h>
/* Strings describing the connection status of a channel */
const char *channel_state_str[4] = {
"not found",
"connection lost",
"connected",
"closed"
};
/* Define a "process variable" (PV)
Note that it now contains two different buffers, info and data.
See /usr/local/epics/base/include/db_access.h for
different dbr_* structures.
We use dbr_ctrl_* to store full information about a PV. This
information normally does not change, it is "static". Thus,
we need to read it only once.
We use dbr_sts_* structures to store the "dynamic" data. This
normally changes frequently. Thus, we read it repeatedly.
We define different types of PVs for different data types.
When reading info and data (via cainfo and caget) we must specify
what data type we have. We use the DBF_* macros for that.
At the moment, only support DBF_DOUBLE, DBF_LONG, DBF_STRING, DBF_ENUM.
Note: The info structure is nice for displaying and formatting a value
but it is not necessary. You can perfectly live without it and use
only dbr_sts_* structures in your program.
But always read at least dbr_sts_* (or longer structures), because it
contains the very important severity information.
If severity is INVALID_ALARM (=3) (see /usr/local/epics/base/include/alarm.h)
the value is really not valid! One reason can be that the driver on the
IOC cannot read the value from the hardware.
Don't ignore that or your application may read rubbish and fail unexpectedly.
*/
typedef struct {
chid channel;
int status;
struct dbr_ctrl_double info;
struct dbr_sts_double data;
} epicsDoublePV;
/* There is more than just double values, e.g. long integers */
typedef struct {
chid channel;
int status;
struct dbr_ctrl_long info;
struct dbr_sts_long data;
} epicsLongPV;
/* Enums are something special: they have a table of strings attached */
typedef struct {
chid channel;
int status;
struct dbr_ctrl_enum info;
struct dbr_sts_enum data;
} epicsEnumPV;
/* Strings don't have a dbr_ctrl_ structure. They are too primitive */
typedef struct {
chid channel;
int status;
struct dbr_sts_string data;
} epicsStringPV;
/* Print the contents of a PV
We have have to print different information for different
types of PVs. Note how static information (e.g. 'units') is taken from info
and dynamic information ('value' and 'severity') is taken from from data.
Also note that the data type of a PV is not necessarily the same as the
native type of the channel. EPICS converts the types automatically.
In a later lesson, we will learn how to select the correct dbr_* structure
automatically from the native channel type. This is useful for interfaces
to scripting languages.
*/
void printDoublePV(const epicsDoublePV* pv)
{
if (ca_state(pv->channel) != cs_conn)
{
printf("%s: <%s>\n",
ca_name(pv->channel), channel_state_str[ca_state(pv->channel)]);
return;
}
/* Print channel name, native channel type,
value, units, severity, range and setrange */
printf("%s (%s as DOUBLE) = %#.*f %s %s range:[%#.*f ... %#.*f] setrange:[%#.*f ... %#.*f]\n",
/* Get name and native type from channel ID */
ca_name(pv->channel), dbf_type_to_text(ca_field_type(pv->channel)),
/* Get static info 'precision' and 'units', and dynamic data 'value' */
pv->info.precision, pv->data.value, pv->info.units,
/* Get dynamic data 'severity' */
epicsAlarmSeverityStrings[pv->data.severity],
/* Get more static infos */
pv->info.precision, pv->info.lower_disp_limit,
pv->info.precision, pv->info.upper_disp_limit,
pv->info.precision, pv->info.lower_ctrl_limit,
pv->info.precision, pv->info.upper_ctrl_limit);
}
void printLongPV(const epicsLongPV* pv)
{
if (ca_state(pv->channel) != cs_conn)
{
printf("%s: <%s>\n",
ca_name(pv->channel), channel_state_str[ca_state(pv->channel)]);
return;
}
/* Print channel name, native channel type,
value, units, severity, range and setrange
Note: In EPICS, LONG means 32 bit, INT and SHORT both mean 16 bit.
*/
printf("%s (%s as LONG) = %i %s %s range:[%i ... %i] setrange:[%i ... %i]\n",
/* This is similar to the above case but uses long everywhere */
ca_name(pv->channel), dbf_type_to_text(ca_field_type(pv->channel)),
pv->data.value, pv->info.units,
epicsAlarmSeverityStrings[pv->data.severity],
pv->info.lower_disp_limit,
pv->info.upper_disp_limit,
pv->info.lower_ctrl_limit,
pv->info.upper_ctrl_limit);
}
void printEnumPV(const epicsEnumPV* pv)
{
int i;
if (ca_state(pv->channel) != cs_conn)
{
printf("%s: <%s>\n",
ca_name(pv->channel), channel_state_str[ca_state(pv->channel)]);
return;
}
/* Print channel name, native channel type,
value as number and as string, severity, and all defined strings.
Note that enums don't have units and instead of a range,
they have a list of strings - one for each state (max 16).
*/
printf("%s (%s as ENUM) = %i = \"%s\" %s %i strings:",
/* Get name and native type from channel ID */
ca_name(pv->channel), dbf_type_to_text(ca_field_type(pv->channel)),
/* Get dynamic data 'value' and convert it to a string using the
static information 'strs'. Never forget to check if value
is within bounds or unexpected "segmentation fault" crashes
may happen.
*/
pv->data.value,
pv->data.value < pv->info.no_str ? pv->info.strs[pv->data.value] : "",
/* Get dynamic data 'severity' */
epicsAlarmSeverityStrings[pv->data.severity],
/* Get all defined stings for this channel (from static information) */
pv->info.no_str);
for (i = 0; i < pv->info.no_str; i++)
{
printf("%s\"%s\"", i>0 ? "," : "", pv->info.strs[i]);
}
printf ("\n");
}
void printStringPV(const epicsStringPV* pv)
{
if (ca_state(pv->channel) != cs_conn)
{
printf("%s: <%s>\n",
ca_name(pv->channel), channel_state_str[ca_state(pv->channel)]);
return;
}
/* Print channel name, native channel type,
value and severity */
printf("%s (%s as STRING) = \"%s\" %s\n",
ca_name(pv->channel), dbf_type_to_text(ca_field_type(pv->channel)),
pv->data.value,
epicsAlarmSeverityStrings[pv->data.severity]);
}
/* Connect a PV to a channel */
#define casearch(name, pv) SEVCHK(\
(pv).status = (ca_search((name), &(pv).channel)), name)
/* Wrapper macros to read 'info' and 'data' fields of a PV
Call cainfo once during initialization to get all available infos.
Later, call caget to get only the dynamic information (value and severity).
Both macros need an additiional DBF_* argument to know the data type.
The dbf_type_to_DBR_* macros from db_access.h are used to translate
DBF_* to the full data structure (either DBR_CTRL_* or DBR_STS_*).
Both macros do nothing when the channel is disconnected.
*/
/* get full information about a PV */
#define cainfo(pv, type) SEVCHK(\
(pv).status = (ca_state((pv).channel) != cs_conn ? ECA_DISCONN : \
ca_get(dbf_type_to_DBR_CTRL(type), (pv).channel, &(pv).info)), ca_name((pv).channel))
/* get only usually changing information about a PV */
#define caget(pv, type) SEVCHK(\
(pv).status = (ca_state((pv).channel) != cs_conn ? ECA_DISCONN : \
ca_get(dbf_type_to_DBR_STS(type), (pv).channel, &(pv).data)), ca_name((pv).channel))
int main()
{
epicsDoublePV gapreadD, gapdoneD;
epicsLongPV gapreadL, gapdoneL;
epicsStringPV gapreadS, gapdoneS;
epicsEnumPV gapreadE, gapdoneE;
double search_timeout = 5.0; /* seconds */
double get_timeout = 1.0; /* seconds */
double loop_period = 5.0; /* seconds */
int num_turns = 6;
int i;
/* Step1: initialize channel access and search for all channels. */
ca_task_initialize();
/* Let's have a look how EPICS conencts to different types of PVs.
We try here to connect an analogue value and a discrete value
to double, long, string and enum PVs. We'll see what happens.
*/
casearch("X10SA-ID-GAP:READ", gapreadD);
casearch("X10SA-ID-GAP:DONE", gapdoneD);
casearch("X10SA-ID-GAP:READ", gapreadL);
casearch("X10SA-ID-GAP:DONE", gapdoneL);
casearch("X10SA-ID-GAP:READ", gapreadS);
casearch("X10SA-ID-GAP:DONE", gapdoneS);
casearch("X10SA-ID-GAP:READ", gapreadE);
casearch("X10SA-ID-GAP:DONE", gapdoneE);
SEVCHK(ca_pend_io(search_timeout), "casearch");
/* Step 2: get available infos */
/* We get all the static information now and the dynamic
information in a loop. That gives us all the infos we
need for nice formatting etc., but saves network bandwidth
in the loop.
Take care that the DBF_* matches the PV type.
Skip the string PVs here because they don't have info.
*/
cainfo(gapreadD, DBF_DOUBLE);
cainfo(gapdoneD, DBF_DOUBLE);
cainfo(gapreadL, DBF_LONG);
cainfo(gapdoneL, DBF_LONG);
cainfo(gapreadE, DBF_ENUM);
cainfo(gapdoneE, DBF_ENUM);
/* The above cainfo calls only fill the 'info' structure of the PV.
Let's as well fill the 'data' structure. Of course we could
as well do this in the loop, but I want to print the values
once before entering the loop.
*/
caget(gapreadD, DBF_DOUBLE);
caget(gapdoneD, DBF_DOUBLE);
caget(gapreadL, DBF_LONG);
caget(gapdoneL, DBF_LONG);
caget(gapreadS, DBF_STRING);
caget(gapdoneS, DBF_STRING);
caget(gapreadE, DBF_ENUM);
caget(gapdoneE, DBF_ENUM);
/* Send all above requests in parallel and wait for reply */
SEVCHK(ca_pend_io(get_timeout), "cainfo");
printf("Init\n");
printDoublePV(&gapreadD);
printDoublePV(&gapdoneD);
printLongPV(&gapreadL);
printLongPV(&gapdoneL);
printStringPV(&gapreadS);
printStringPV(&gapdoneS);
printEnumPV(&gapreadE);
printEnumPV(&gapdoneE);
/* Step 3: enter the main loop */
for (i = 1; i <= num_turns; i++)
{
/* Wait some time while doing channel access in the background.
This allows to handle disconnection/connection and other
"background activity" while we wait. We will learn more
about this in future lessons when we use callbacks and
monitors.
*/
printf("Waiting for %g seconds\n", loop_period);
ca_pend_event(loop_period);
/* Get all current values and print them */
printf("Turn %d/%d\n", i, num_turns);
caget(gapreadD, DBR_DOUBLE);
caget(gapdoneD, DBR_DOUBLE);
caget(gapreadL, DBR_LONG);
caget(gapdoneL, DBR_LONG);
caget(gapreadS, DBR_STRING);
caget(gapdoneS, DBR_STRING);
caget(gapreadE, DBR_ENUM);
caget(gapdoneE, DBR_ENUM);
SEVCHK(ca_pend_io(get_timeout), "caget");
printDoublePV(&gapreadD);
printDoublePV(&gapdoneD);
printLongPV(&gapreadL);
printLongPV(&gapdoneL);
printStringPV(&gapreadS);
printStringPV(&gapdoneS);
printEnumPV(&gapreadE);
printEnumPV(&gapdoneE);
}
/* Last step: free all channel access resources */
printf("Done\n");
ca_task_exit();
return 0;
}