458 lines
13 KiB
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;
|
|
}
|