Copy of old lessons from afs webpage
This commit is contained in:
@@ -0,0 +1,560 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define epicsAlarmGLOBAL
|
||||
#include "epicsPV.h"
|
||||
|
||||
static int contextCreated = 0;
|
||||
static unsigned long pendingConnects = 0;
|
||||
static unsigned long pendingGets = 0;
|
||||
|
||||
const char* PVdataTypeStrings[9] = {
|
||||
"STRING",
|
||||
"SHORT",
|
||||
"FLOAT",
|
||||
"ENUM",
|
||||
"CHAR",
|
||||
"LONG",
|
||||
"DOUBLE",
|
||||
"NO_ACCESS",
|
||||
"UNDEFINED"
|
||||
};
|
||||
|
||||
const char* PVconnectionStateStrings [4] = {
|
||||
"never connected",
|
||||
"disconnected",
|
||||
"connected",
|
||||
"closed"
|
||||
};
|
||||
|
||||
struct epicsPV_s {
|
||||
chid channel;
|
||||
void* data;
|
||||
void* info;
|
||||
caDatatype usedDatatype;
|
||||
caDatatype requestedDatatype;
|
||||
int status;
|
||||
char stringrep[40];
|
||||
PVconnectionCallback connectionCallback;
|
||||
void* userarg;
|
||||
};
|
||||
|
||||
epicsPV PVcreateWithTypeAndConnectionCallback(const char* channelName,caDatatype preferredDatatype,
|
||||
PVconnectionCallback cb, void* userarg)
|
||||
{
|
||||
epicsPV pv;
|
||||
|
||||
if (!contextCreated)
|
||||
{
|
||||
ca_context_create(ca_disable_preemptive_callback);
|
||||
atexit(ca_context_destroy);
|
||||
contextCreated = 1;
|
||||
}
|
||||
pv = malloc(sizeof (struct epicsPV_s));
|
||||
pv->channel = NULL;
|
||||
pv->data = NULL;
|
||||
pv->info = NULL;
|
||||
pv->usedDatatype = caTypeNative;
|
||||
pv->requestedDatatype = caTypeNative;
|
||||
pv->status = ECA_DISCONN;
|
||||
pv->connectionCallback = cb;
|
||||
pv->userarg = userarg;
|
||||
if (channelName) PVlinkWithType(pv, channelName, preferredDatatype);
|
||||
return pv;
|
||||
}
|
||||
|
||||
void PVdestroy(epicsPV pv)
|
||||
{
|
||||
if (!pv) return;
|
||||
PVunlink(pv);
|
||||
free(pv);
|
||||
}
|
||||
|
||||
static void connectCallback(struct connection_handler_args args)
|
||||
{
|
||||
epicsPV pv = ca_puser(args.chid);
|
||||
int up = (args.op == CA_OP_CONN_UP);
|
||||
|
||||
if (up)
|
||||
{
|
||||
if (pv->usedDatatype == caTypeNative)
|
||||
pendingConnects --;
|
||||
if (pv->requestedDatatype != caTypeNative)
|
||||
pv->usedDatatype = pv->requestedDatatype;
|
||||
else
|
||||
pv->usedDatatype = ca_field_type(pv->channel);
|
||||
|
||||
pv->data = realloc(pv->data, dbr_size_n(dbf_type_to_DBR_TIME(pv->usedDatatype),
|
||||
ca_element_count(pv->channel)));
|
||||
if (!pv->data)
|
||||
{
|
||||
pv->status = ECA_ALLOCMEM;
|
||||
return;
|
||||
}
|
||||
if (pv->usedDatatype != DBF_STRING)
|
||||
{
|
||||
pv->info = realloc(pv->info, dbr_size_n(dbf_type_to_DBR_CTRL(pv->usedDatatype),1));
|
||||
if (!pv->info)
|
||||
{
|
||||
pv->status = ECA_ALLOCMEM;
|
||||
return;
|
||||
}
|
||||
pv->status = ca_get(dbf_type_to_DBR_CTRL(pv->usedDatatype), pv->channel, pv->info);
|
||||
}
|
||||
else
|
||||
{
|
||||
free(pv->info);
|
||||
pv->info = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pv->status = ECA_DISCONN;
|
||||
}
|
||||
if (pv->connectionCallback)
|
||||
{
|
||||
pv->connectionCallback(pv, up, pv->userarg);
|
||||
}
|
||||
}
|
||||
|
||||
int PVlinkWithType(epicsPV pv, const char* channelName, caDatatype preferredDatatype)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!pv) return ECA_INTERNAL;
|
||||
pv->requestedDatatype = preferredDatatype;
|
||||
if (pv->channel) PVunlink(pv);
|
||||
if (!channelName) return ECA_NORMAL;
|
||||
status = ca_create_channel(channelName,
|
||||
connectCallback, pv,
|
||||
CA_PRIORITY_DEFAULT,
|
||||
&pv->channel);
|
||||
if (status == ECA_NORMAL)
|
||||
{
|
||||
pendingConnects ++;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void PVunlink(epicsPV pv)
|
||||
{
|
||||
if (pv && pv->channel)
|
||||
{
|
||||
if (pv->connectionCallback)
|
||||
{
|
||||
pv->connectionCallback(pv, 0, pv->userarg);
|
||||
}
|
||||
ca_clear_channel(pv->channel);
|
||||
pv->channel = NULL;
|
||||
free(pv->data);
|
||||
pv->data = NULL;
|
||||
free(pv->info);
|
||||
pv->info = NULL;
|
||||
pv->status = ECA_DISCONN;
|
||||
if (pv->usedDatatype != caTypeNative)
|
||||
{
|
||||
pendingConnects --;
|
||||
}
|
||||
pv->usedDatatype = caTypeNative;
|
||||
}
|
||||
}
|
||||
|
||||
double PVwaitForConnect(epicsPV pv, double timeoutSec)
|
||||
{
|
||||
ca_poll();
|
||||
double wait = 0.001;
|
||||
while (timeoutSec != 0.0 &&
|
||||
(pv ? ca_state(pv->channel) == cs_never_conn : pendingConnects != 0))
|
||||
{
|
||||
if (timeoutSec > 0.0 && timeoutSec < wait) wait = timeoutSec;
|
||||
ca_pend_event(wait);
|
||||
if (timeoutSec > 0.0) timeoutSec -= wait;
|
||||
wait *= 1.2;
|
||||
if (wait > 0.5) wait = 0.5;
|
||||
}
|
||||
return timeoutSec;
|
||||
}
|
||||
|
||||
const char* PVname(epicsPV pv)
|
||||
{
|
||||
return pv && pv->channel ? ca_name(pv->channel) : "";
|
||||
}
|
||||
|
||||
int PVerrorStatus(epicsPV pv)
|
||||
{
|
||||
return pv ? pv->status : ECA_BADCHID;
|
||||
}
|
||||
|
||||
enum channel_state PVconnectionState(epicsPV pv)
|
||||
{
|
||||
return pv && pv->channel ? ca_state(pv->channel) : cs_never_conn;
|
||||
}
|
||||
|
||||
caDatatype PVdatatype(epicsPV pv)
|
||||
{
|
||||
return pv ? pv->usedDatatype : caTypeNative;
|
||||
}
|
||||
|
||||
unsigned long PVelementCount(epicsPV pv)
|
||||
{
|
||||
return pv && pv->channel ? ca_element_count(pv->channel) : 0;
|
||||
}
|
||||
|
||||
const char* PVunits(epicsPV pv)
|
||||
{
|
||||
if (!pv->info) return "";
|
||||
switch (pv->usedDatatype)
|
||||
{
|
||||
case DBF_CHAR:
|
||||
return ((struct dbr_ctrl_char*)(pv->info))->units;
|
||||
case DBF_SHORT:
|
||||
return ((struct dbr_ctrl_short*)(pv->info))->units;
|
||||
case DBF_LONG:
|
||||
return ((struct dbr_ctrl_long*)(pv->info))->units;
|
||||
case DBF_FLOAT:
|
||||
return ((struct dbr_ctrl_float*)(pv->info))->units;
|
||||
case DBF_DOUBLE:
|
||||
return ((struct dbr_ctrl_double*)(pv->info))->units;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
int PVprecision(epicsPV pv)
|
||||
{
|
||||
if (!pv->info) return 0;
|
||||
switch (pv->usedDatatype)
|
||||
{
|
||||
case DBF_FLOAT:
|
||||
return ((struct dbr_ctrl_float*)(pv->info))->precision;
|
||||
case DBF_DOUBLE:
|
||||
return ((struct dbr_ctrl_double*)(pv->info))->precision;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int PVhasReadAccess(epicsPV pv)
|
||||
{
|
||||
return pv && pv->channel ? ca_read_access(pv->channel) : 0;
|
||||
}
|
||||
|
||||
int PVhasWriteAccess(epicsPV pv)
|
||||
{
|
||||
return pv && pv->channel ? ca_write_access(pv->channel) : 0;
|
||||
}
|
||||
|
||||
epicsAlarmSeverity PValarmSeverity(epicsPV pv)
|
||||
{
|
||||
if (pv && pv->data)
|
||||
{
|
||||
epicsUInt16 sevr = ((struct dbr_time_string*)(pv->data))->severity;
|
||||
if (sevr <= lastEpicsAlarmSev) return sevr;
|
||||
}
|
||||
return epicsSevInvalid;
|
||||
}
|
||||
|
||||
epicsAlarmCondition PValarmStatus(epicsPV pv)
|
||||
{
|
||||
if (pv && pv->data)
|
||||
{
|
||||
epicsUInt16 stat = ((struct dbr_time_string*)(pv->data))->status;
|
||||
if (stat <= lastEpicsAlarmCond) return stat;
|
||||
}
|
||||
return epicsAlarmUDF;
|
||||
}
|
||||
|
||||
const epicsTimeStamp* PVtimestamp(epicsPV pv)
|
||||
{
|
||||
static const epicsTimeStamp zerotime = {0,0};
|
||||
if (pv && pv->data)
|
||||
{
|
||||
return &((struct dbr_time_string*)(pv->data))->stamp;
|
||||
}
|
||||
return &zerotime;
|
||||
}
|
||||
|
||||
double PVtoDoubleElement(epicsPV pv, unsigned long index)
|
||||
{
|
||||
if (!pv || !pv->data || index >= ca_element_count(pv->channel))
|
||||
{
|
||||
return atof("NAN");
|
||||
}
|
||||
switch (pv->usedDatatype)
|
||||
{
|
||||
case DBF_CHAR:
|
||||
return (&((struct dbr_time_char*)(pv->data))->value)[index];
|
||||
case DBF_SHORT:
|
||||
return (&((struct dbr_time_short*)(pv->data))->value)[index];
|
||||
case DBF_LONG:
|
||||
return (&((struct dbr_time_long*)(pv->data))->value)[index];
|
||||
case DBF_FLOAT:
|
||||
return (&((struct dbr_time_float*)(pv->data))->value)[index];
|
||||
case DBF_DOUBLE:
|
||||
return (&((struct dbr_time_double*)(pv->data))->value)[index];
|
||||
case DBF_ENUM:
|
||||
return (&((struct dbr_time_enum*)(pv->data))->value)[index];
|
||||
case DBF_STRING:
|
||||
return atof((&((struct dbr_time_string*)(pv->data))->value)[index]);
|
||||
default:
|
||||
return atof("NAN");
|
||||
}
|
||||
}
|
||||
|
||||
long PVtoLongElement(epicsPV pv, unsigned long index)
|
||||
{
|
||||
if (!pv || !pv->data || index >= ca_element_count(pv->channel))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
switch (pv->usedDatatype)
|
||||
{
|
||||
case DBF_CHAR:
|
||||
return (&((struct dbr_time_char*)(pv->data))->value)[index];
|
||||
case DBF_SHORT:
|
||||
return (&((struct dbr_time_short*)(pv->data))->value)[index];
|
||||
case DBF_LONG:
|
||||
return (&((struct dbr_time_long*)(pv->data))->value)[index];
|
||||
case DBF_FLOAT:
|
||||
return (&((struct dbr_time_float*)(pv->data))->value)[index];
|
||||
case DBF_DOUBLE:
|
||||
return (&((struct dbr_time_double*)(pv->data))->value)[index];
|
||||
case DBF_ENUM:
|
||||
return (&((struct dbr_time_enum*)(pv->data))->value)[index];
|
||||
case DBF_STRING:
|
||||
return atoi((&((struct dbr_time_string*)(pv->data))->value)[index]);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char* PVtoStringElement(epicsPV pv, int flags, unsigned long index)
|
||||
{
|
||||
int prec;
|
||||
double val;
|
||||
long ival;
|
||||
struct dbr_ctrl_enum* enuminfo;
|
||||
const char* units;
|
||||
|
||||
if (!pv || !pv->data) return "<not connected>";
|
||||
if (index > ca_element_count(pv->channel)) return "<out of bounds>";
|
||||
switch (pv->usedDatatype)
|
||||
{
|
||||
case DBF_CHAR:
|
||||
ival = (&((struct dbr_time_char*)(pv->data))->value)[index];
|
||||
units = ((struct dbr_ctrl_char*)(pv->info))->units;
|
||||
goto printint;
|
||||
case DBF_SHORT:
|
||||
ival = (&((struct dbr_time_short*)(pv->data))->value)[index];
|
||||
units = ((struct dbr_ctrl_short*)(pv->info))->units;
|
||||
goto printint;
|
||||
case DBF_LONG:
|
||||
ival = (&((struct dbr_time_long*)(pv->data))->value)[index];
|
||||
units = ((struct dbr_ctrl_long*)(pv->info))->units;
|
||||
printint:
|
||||
if (flags & PV_WITHUNITS && units[0])
|
||||
sprintf(pv->stringrep, "%ld %s", ival, units);
|
||||
else
|
||||
sprintf(pv->stringrep, "%ld", ival);
|
||||
break;
|
||||
case DBF_FLOAT:
|
||||
val = (&((struct dbr_time_float*)(pv->data))->value)[index];
|
||||
prec = ((struct dbr_ctrl_float*)(pv->info))->precision;
|
||||
units = ((struct dbr_ctrl_float*)(pv->info))->units;
|
||||
goto printdouble;
|
||||
case DBF_DOUBLE:
|
||||
val = (&((struct dbr_time_double*)(pv->data))->value)[index];
|
||||
prec = ((struct dbr_ctrl_double*)(pv->info))->precision;
|
||||
units = ((struct dbr_ctrl_double*)(pv->info))->units;
|
||||
printdouble:
|
||||
if (prec > 17) prec = -17;
|
||||
if (prec < -17) prec = -17;
|
||||
if (flags & PV_WITHUNITS && units[0])
|
||||
{
|
||||
if (prec >= 0)
|
||||
sprintf(pv->stringrep, "%.*f %s", prec, val, units);
|
||||
else
|
||||
sprintf(pv->stringrep, "%.*g %s", -prec, val, units);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prec >= 0)
|
||||
sprintf(pv->stringrep, "%.*f", prec, val);
|
||||
else
|
||||
sprintf(pv->stringrep, "%.*g", -prec, val);
|
||||
}
|
||||
break;
|
||||
case DBF_STRING:
|
||||
return (&((struct dbr_time_string*)(pv->data))->value)[index];
|
||||
case DBF_ENUM:
|
||||
ival = (&((struct dbr_time_enum*)(pv->data))->value)[index];
|
||||
enuminfo = (struct dbr_ctrl_enum*)(pv->info);
|
||||
if (ival < enuminfo->no_str)
|
||||
return enuminfo->strs[ival];
|
||||
sprintf(pv->stringrep, "%ld", ival);
|
||||
break;
|
||||
default:
|
||||
return "<not accessible>";
|
||||
}
|
||||
return pv->stringrep;
|
||||
}
|
||||
|
||||
static void getCallback(struct event_handler_args args)
|
||||
{
|
||||
epicsPV pv = args.usr;
|
||||
|
||||
printf("updating %s %ld elements\n", ca_name(pv->channel), args.count);
|
||||
if (pv->status == ECA_IOINPROGRESS)
|
||||
{
|
||||
pendingGets--;
|
||||
}
|
||||
pv->status = args.status;
|
||||
if (args.status != ECA_NORMAL)
|
||||
{
|
||||
/* Something went wrong. */
|
||||
fprintf(stderr, "getCallback %s: %s\n",
|
||||
ca_name(pv->channel), ca_message(args.status));
|
||||
return;
|
||||
}
|
||||
if (args.chid != pv->channel)
|
||||
{
|
||||
fprintf(stderr, "INTERNAL ERROR in updateValueCallback %s: got unexpected chid\n",
|
||||
ca_name(pv->channel));
|
||||
return;
|
||||
}
|
||||
if (args.type != dbf_type_to_DBR_TIME(pv->usedDatatype))
|
||||
{
|
||||
fprintf(stderr, "INTERNAL ERROR in updateValueCallback %s: got unexpected type\n",
|
||||
ca_name(pv->channel));
|
||||
return;
|
||||
}
|
||||
memcpy(pv->data, args.dbr, dbr_size_n(args.type, args.count));
|
||||
}
|
||||
|
||||
int cagetList(epicsPV pv1, ...)
|
||||
{
|
||||
va_list ap;
|
||||
epicsPV pv;
|
||||
unsigned long faults = 0;
|
||||
double timeoutSec = 2.0;
|
||||
double wait = 0.001;
|
||||
|
||||
/* Prepate get requests */
|
||||
va_start(ap, pv1);
|
||||
pv = pv1;
|
||||
while (pv)
|
||||
{
|
||||
if (!pv->channel)
|
||||
{
|
||||
/* not assigned */
|
||||
pv->status = ECA_BADCHID;
|
||||
faults ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ca_state(pv->channel) == cs_never_conn)
|
||||
{
|
||||
/* try to connect for the first time */
|
||||
timeoutSec = PVwaitForConnect(pv, timeoutSec);
|
||||
}
|
||||
if (ca_state(pv->channel) == cs_conn)
|
||||
{
|
||||
pv->status = ca_array_get_callback(
|
||||
dbf_type_to_DBR_TIME(pv->usedDatatype),
|
||||
ca_element_count(pv->channel),
|
||||
pv->channel,
|
||||
getCallback, pv);
|
||||
if (pv->status != ECA_NORMAL)
|
||||
{
|
||||
/* io error */
|
||||
fprintf(stderr, "%s can't do get: %s\n",
|
||||
ca_name(pv->channel), ca_message(pv->status));
|
||||
faults ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pv->status = ECA_IOINPROGRESS;
|
||||
pendingGets++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pv->status = ECA_DISCONN;
|
||||
faults++;
|
||||
}
|
||||
}
|
||||
pv = va_arg(ap, epicsPV);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
/* Wait for values */
|
||||
timeoutSec = 2.0;
|
||||
ca_poll();
|
||||
while (timeoutSec != 0.0 && pendingGets != 0)
|
||||
{
|
||||
if (timeoutSec > 0.0 && timeoutSec < wait) wait = timeoutSec;
|
||||
printf("waiting for %g seconds\n", wait);
|
||||
ca_pend_event(wait);
|
||||
if (timeoutSec > 0.0) timeoutSec -= wait;
|
||||
wait *= 1.2;
|
||||
if (wait > 0.5) wait = 0.5;
|
||||
}
|
||||
|
||||
/* Check results */
|
||||
va_start(ap, pv1);
|
||||
pv = pv1;
|
||||
while (pv)
|
||||
{
|
||||
if (pv->status == ECA_IOINPROGRESS)
|
||||
{
|
||||
pv->status = ECA_TIMEOUT;
|
||||
faults++;
|
||||
pendingGets--;
|
||||
}
|
||||
pv = va_arg(ap, epicsPV);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
printf ("pendingGets = %ld, faults = %ld\n",
|
||||
pendingGets, faults);
|
||||
return faults;
|
||||
}
|
||||
|
||||
/*
|
||||
epicsPV& epicsPV::put(const void* value, unsigned long elements, caDatatype datatype, double timeoutSec)
|
||||
{
|
||||
if (!channel)
|
||||
throw runtime_error("epicsPV::put: PV not linked to a channel");
|
||||
switch (connectionState())
|
||||
{
|
||||
case cs_conn:
|
||||
break;
|
||||
case cs_never_conn:
|
||||
timeoutSec = waitForConnect(timeoutSec, this);
|
||||
if (connectionState() == cs_conn) break;
|
||||
default:
|
||||
throw runtime_error(string("epicsPV::put: ")+ca_name(channel)+" "+connectionStateStr());
|
||||
}
|
||||
if (!hasWriteAccess())
|
||||
throw runtime_error(string("epicsPV::put: ")+ca_name(channel)+" not writable");
|
||||
cout << "putting " << name() << ", elements: " << elements << ", datatype: " << datatype << " ";
|
||||
switch (datatype)
|
||||
{
|
||||
case caTypeDouble:
|
||||
cout << *(const double*)value;
|
||||
break;
|
||||
case caTypeLong:
|
||||
cout << *(const long*)value;
|
||||
break;
|
||||
case caTypeString:
|
||||
cout << (const char*)value;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
cout << endl;
|
||||
SEVCHK(ca_array_put(datatype, elements, channel, value), "epicsPV::put");
|
||||
ca_pend_io(timeoutSec);
|
||||
return *this;
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user