Copy of old lessons from afs webpage
This commit is contained in:
@@ -0,0 +1,457 @@
|
||||
/* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user