Files
caClientLessons/caLesson5/caLesson5.c

458 lines
13 KiB
C

/* caLesson5.c
by Dirk Zimoch, 2007
*/
#include <stdio.h>
#include <stdlib.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)
This time, the PV should be generic. There should be only one
type of PV for all possible data types. That means we need to
find out the data type before actually allocating memory for
the data.
Especially interfaces to scipting languages, such as TCL or IDL
have to use generic PVs, because at the time the interface is
compiled we cannot know to what type of channel the user will
connect later.
Again, we use dbr_ctrl_* structures for "static" data and
dbr_sts_* structures for "dynamic" data.
At compile-time, we don't know the exact data type of 'info' and
'data'. Thus, we have to use void* and cast to the correct type.
*/
typedef struct {
chid channel;
int status;
union {
struct dbr_ctrl_short SHORT;
struct dbr_ctrl_float FLOAT;
struct dbr_ctrl_enum ENUM;
struct dbr_ctrl_char CHAR;
struct dbr_ctrl_long LONG;
struct dbr_ctrl_double DOUBLE;
} *info;
union {
struct dbr_sts_string STRING;
struct dbr_sts_short SHORT;
struct dbr_sts_float FLOAT;
struct dbr_sts_enum ENUM;
struct dbr_sts_char CHAR;
struct dbr_sts_long LONG;
struct dbr_sts_double DOUBLE;
} *data;
} epicsPV;
/* Define some macros to get infos out of a generic PV
*/
#define PV_name(pv) (ca_name((pv)->channel))
#define PV_state(pv) (ca_state((pv)->channel))
#define PV_has_info(pv) ((pv)->info != NULL)
#define PV_has_data(pv) ((pv)->data != NULL)
#define PV_type(pv) (ca_field_type((pv)->channel))
#define PV_status(pv) ((pv)->data->SHORT.status)
#define PV_severity(pv) ((pv)->data->SHORT.severity)
#define PV_value(pv,type) ((pv)->data->type.value)
#define PV_info(pv,type) ((pv)->info->type)
/* Get the units out of the PV.
When we don't know return an empty string.
*/
const char* PV_units(epicsPV* pv)
{
if (!pv || !pv->info) return "";
switch (PV_type(pv))
{
case DBF_SHORT:
return pv->info->SHORT.units;
case DBF_FLOAT:
return pv->info->FLOAT.units;
case DBF_CHAR:
return pv->info->CHAR.units;
case DBF_LONG:
return pv->info->LONG.units;
case DBF_DOUBLE:
return pv->info->DOUBLE.units;
default:
return "";
}
}
/* Get the precision out of the PV.
When we don't know return something reasonable. */
short PV_precision(epicsPV* pv)
{
if (!pv) return 0;
switch (PV_type(pv))
{
case DBF_FLOAT:
if (!pv->info) return 4;
return ((struct dbr_ctrl_float*)pv->info)->precision;
case DBF_DOUBLE:
if (!pv->info) return 6;
return ((struct dbr_ctrl_double*)pv->info)->precision;
default:
return 0;
}
}
int cainfo(epicsPV* pv)
{
if (PV_state(pv) != cs_conn)
{
printf ("cainfo %s: %s\n", PV_name(pv),
channel_state_str[PV_state(pv)]);
return ECA_DISCONN;
}
/* Read static info only once. */
if (!pv->info && PV_type(pv) != DBF_STRING)
{
int dbr_type;
dbr_type = dbf_type_to_DBR_CTRL(PV_type(pv));
if (!pv->info)
{
printf ("cainfo %s: allocating memory for static data\n", PV_name(pv));
pv->info = malloc(dbr_size[dbr_type]);
if (!pv->info) return ECA_ALLOCMEM;
}
printf ("cainfo %s: requesting static data\n", PV_name(pv));
return ca_get(dbr_type, pv->channel, pv->info);
}
return ECA_NORMAL;
}
int caget(epicsPV* pv)
{
int dbr_type;
if (PV_state(pv) != cs_conn)
{
printf ("caget %s: %s\n", PV_name(pv),
channel_state_str[PV_state(pv)]);
return ECA_DISCONN;
}
/* Allocate memory only once but read dynamic data every time */
dbr_type = dbf_type_to_DBR_STS(PV_type(pv));
if (!pv->data)
{
printf ("caget %s: allocating memory for dynamic data\n", PV_name(pv));
pv->data = malloc(dbr_size[dbr_type]);
if (!pv->data) return ECA_ALLOCMEM;
}
printf ("caget %s: requesting dynamic data\n", PV_name(pv));
return ca_get(dbr_type, pv->channel, pv->data);
}
int camonitor(epicsPV* pv, void (*monitor)(epicsPV* pv))
{
return ECA_NORMAL;
}
/* Print the contents of a generic PV.
*/
void printPV(epicsPV* pv)
{
const char* name;
const char* status_str;
const char* severity_str;
const char* units;
short precision;
name = PV_name(pv);
if (!pv)
{
printf("<NULL PV>\n");
}
if (PV_state(pv) != cs_conn)
{
/* Channel is not connected */
printf("%s: <%s>\n",
name, channel_state_str[PV_state(pv)]);
return;
}
if (!pv->data)
{
printf("%s: <caget never called>\n", name);
return;
}
status_str = epicsAlarmConditionStrings[PV_status(pv)];
severity_str = epicsAlarmSeverityStrings[PV_severity(pv)];
units = PV_units(pv);
precision = PV_precision(pv);
/* Handle different data types.
See /usr/local/epics/base/include/db_access.h for DBF_* types.
For each possible type, we have to cast 'info' and 'data'
to the correct structures before we can access the data.
*/
switch (PV_type(pv))
{
case DBF_STRING:
{
/* Print channel name, native channel type,
value and severity */
printf("%s (STRING) = \"%s\" %s:%s\n",
name,
PV_value(pv,STRING),
severity_str, status_str);
break;
}
case DBF_SHORT:
{
printf("%s (SHORT) = %hi %s %s:%s",
name,
PV_value(pv,SHORT), units,
severity_str, status_str);
if (PV_has_info(pv)) printf(" range:[%hi ... %hi] setrange:[%hi ... %hi]\n",
PV_info(pv,SHORT).lower_disp_limit,
PV_info(pv,SHORT).upper_disp_limit,
PV_info(pv,SHORT).lower_ctrl_limit,
PV_info(pv,SHORT).upper_ctrl_limit);
else printf("\n");
break;
}
case DBF_FLOAT:
{
printf("%s (FLOAT) = %#.*f %s %s:%s",
name,
precision, PV_value(pv,FLOAT), units,
severity_str, status_str);
if (PV_has_info(pv)) printf(" range:[%#.*f ... %#.*f] setrange:[%#.*f ... %#.*f]\n",
precision, PV_info(pv,FLOAT).lower_disp_limit,
precision, PV_info(pv,FLOAT).upper_disp_limit,
precision, PV_info(pv,FLOAT).lower_ctrl_limit,
precision, PV_info(pv,FLOAT).upper_ctrl_limit);
else printf("\n");
break;
}
case DBF_ENUM:
{
int i;
if (PV_has_info(pv))
{
printf("%s (ENUM) = %i = \"%s\" %s:%s %i strings:",
name,
PV_value(pv,ENUM),
PV_value(pv,ENUM) < PV_info(pv,ENUM).no_str ?
PV_info(pv,ENUM).strs[PV_value(pv,ENUM)] : "",
severity_str, status_str,
PV_info(pv,ENUM).no_str);
for (i = 0; i < PV_info(pv,ENUM).no_str; i++)
{
printf("%s\"%s\"", i>0 ? "," : "", PV_info(pv,ENUM).strs[i]);
}
printf ("\n");
}
else printf("%s (ENUM) = %i %s:%s\n",
name,
data->value,
severity_str, status_str);
break;
}
case DBF_CHAR:
{
struct dbr_ctrl_char* info = pv->info;
struct dbr_sts_char* data = pv->data;
/* Print channel name, native channel type,
value, units, severity, and ranges */
printf("%s (CHAR) = %i %s %s:%s",
name,
data->value, units,
severity_str, status_str);
if (PV_has_info(pv)) printf(" range:[%i ... %i] setrange:[%i ... %i]\n",
info->lower_disp_limit,
info->upper_disp_limit,
info->lower_ctrl_limit,
info->upper_ctrl_limit);
else printf("\n");
break;
}
case DBF_LONG:
{
struct dbr_ctrl_long* info = pv->info;
struct dbr_sts_long* data = pv->data;
/* Print channel name, native channel type,
value, units, severity, and ranges */
printf("%s (LONG) = %i %s %s:%s",
name,
data->value, units,
severity_str, status_str);
if (info) printf(" range:[%i ... %i] setrange:[%i ... %i]\n",
info->lower_disp_limit,
info->upper_disp_limit,
info->lower_ctrl_limit,
info->upper_ctrl_limit);
else printf("\n");
break;
}
case DBF_DOUBLE:
{
struct dbr_ctrl_double* info = pv->info;
struct dbr_sts_double* data = pv->data;
/* Print channel name, native channel type,
value, units, severity, and ranges */
printf("%s (DOUBLE) = %#.*f %s %s:%s",
name,
precision, data->value, units,
severity_str, status_str);
if (info) printf(" range:[%#.*f ... %#.*f] setrange:[%#.*f ... %#.*f]\n",
precision, info->lower_disp_limit,
precision, info->upper_disp_limit,
precision, info->lower_ctrl_limit,
precision, info->upper_ctrl_limit);
else printf("\n");
break;
}
}
}
epicsPV* newPV(const char* name)
{
int status;
epicsPV* pv = malloc(sizeof(epicsPV));
pv->info = NULL;
pv->data = NULL;
printf("searching for channel %s\n", name);
status = ca_search(name, &pv->channel);
/* status = ca_search(name, &pv->channel); */
SEVCHK(status, "ca_search_and_connect");
if (status != ECA_NORMAL)
{
free(pv);
return NULL;
}
return pv;
}
int main()
{
epicsPV *gapread, *gapdone;
double search_timeout = 5.0; /* seconds */
double get_timeout = 1.0; /* seconds */
double loop_period = 5.0; /* seconds */
int num_turns = 3;
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.
*/
gapread = newPV("X10SA-ID-GAP:READ");
gapdone = newPV("X10SA-ID-GAP:DONE");
/* Send all the search requests but don't wait for connection. */
printf("sending search request\n");
ca_flush_io();
printf("doing other stuff ...\n");
/* Now, connection is done in the background.
We can use the time to initialize the rest of the program,
read files, setup the GUI, etc ... Whatever can be done
before the channels have connected.
*/
printf("finishing search request ...\n");
SEVCHK(ca_pend_io(search_timeout), "search");
printf("searching done\n");
/* Step2: get all static infos */
/*
SEVCHK(cainfo(gapread),"cainfo gapread");
SEVCHK(cainfo(gapdone),"cainfo gapdone");
SEVCHK(ca_pend_io(get_timeout), "cainfo");
*/
/* Step3: reading dynamic data periodically */
printf("\nEntering loop without static infos\n");
for (i=1; i <= num_turns; i++)
{
printf("\nTurn %d/%d\n", i, num_turns);
SEVCHK(caget(gapread), "caget gapread");
SEVCHK(caget(gapdone), "caget gapdone");
SEVCHK(ca_pend_io(get_timeout), "caget");
printPV(gapread);
printPV(gapdone);
ca_pend_event(loop_period);
}
printf("\nFinished loop\n");
SEVCHK(cainfo(gapread),"cainfo gapread");
SEVCHK(cainfo(gapdone),"cainfo gapdone");
SEVCHK(ca_pend_io(get_timeout), "cainfo");
printf("\nEntering loop with static infos\n");
for (i=1; i <= num_turns; i++)
{
printf("\nTurn %d/%d\n", i, num_turns);
SEVCHK(caget(gapread), "caget gapread");
SEVCHK(caget(gapdone), "caget gapdone");
SEVCHK(ca_pend_io(get_timeout), "caget");
printPV(gapread);
printPV(gapdone);
ca_pend_event(loop_period);
}
printf("\nFinished loop\n");
camonitor(gapread, printPV);
camonitor(gapdone, printPV);
cainfo(gapread);
caget(gapread);
cainfo(gapdone);
caget(gapdone);
ca_pend_io(get_timeout);
printPV(gapread);
printPV(gapdone);
printf("doing channel access forever ...\n");
ca_pend_event(0.0);
/* Last step: free all channel access resources */
printf("Done\n");
ca_task_exit();
return 0;
}