Copy of old lessons from afs webpage
This commit is contained in:
@@ -0,0 +1,572 @@
|
||||
#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;
|
||||
static unsigned long pendingPuts = 0;
|
||||
epicsTimeStamp PVinvalidTimestamp = {0,0};
|
||||
|
||||
const char* PVdataTypeStrings[9] = {
|
||||
"STRING",
|
||||
"SHORT",
|
||||
"FLOAT",
|
||||
"ENUM",
|
||||
"CHAR",
|
||||
"LONG",
|
||||
"DOUBLE",
|
||||
"NO_ACCESS",
|
||||
"UNDEFINED"
|
||||
};
|
||||
|
||||
const char* PVconnectionStateStrings [4] = {
|
||||
"never connected",
|
||||
"disconnected",
|
||||
"connected",
|
||||
"closed"
|
||||
};
|
||||
|
||||
epicsPV PVcreateWithTypeAndCallback(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->units = "";
|
||||
pv->precision = 0;
|
||||
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;
|
||||
}
|
||||
pv->units = "";
|
||||
pv->precision = 0;
|
||||
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);
|
||||
switch (pv->usedDatatype)
|
||||
{
|
||||
case DBF_CHAR:
|
||||
pv->units = pv->info_CHAR->units;
|
||||
break;
|
||||
case DBF_SHORT:
|
||||
pv->units = pv->info_SHORT->units;
|
||||
break;
|
||||
case DBF_LONG:
|
||||
pv->units = pv->info_LONG->units;
|
||||
break;
|
||||
case DBF_FLOAT:
|
||||
pv->units = pv->info_FLOAT->units;
|
||||
pv->precision = pv->info_FLOAT->precision;
|
||||
break;
|
||||
case DBF_DOUBLE:
|
||||
pv->units = pv->info_DOUBLE->units;
|
||||
pv->precision = pv->info_DOUBLE->precision;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
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->units = "";
|
||||
pv->precision = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
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 (&pv->CHAR->value)[index];
|
||||
case DBF_SHORT:
|
||||
return (&pv->SHORT->value)[index];
|
||||
case DBF_LONG:
|
||||
return (&pv->LONG->value)[index];
|
||||
case DBF_FLOAT:
|
||||
return (&pv->FLOAT->value)[index];
|
||||
case DBF_DOUBLE:
|
||||
return (&pv->DOUBLE->value)[index];
|
||||
case DBF_ENUM:
|
||||
return (&pv->ENUM->value)[index];
|
||||
case DBF_STRING:
|
||||
return atof((&pv->STRING->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 (&pv->CHAR->value)[index];
|
||||
case DBF_SHORT:
|
||||
return (&pv->SHORT->value)[index];
|
||||
case DBF_LONG:
|
||||
return (&pv->LONG->value)[index];
|
||||
case DBF_FLOAT:
|
||||
return (&pv->FLOAT->value)[index];
|
||||
case DBF_DOUBLE:
|
||||
return (&pv->DOUBLE->value)[index];
|
||||
case DBF_ENUM:
|
||||
return (&pv->ENUM->value)[index];
|
||||
case DBF_STRING:
|
||||
return atoi((&pv->STRING->value)[index]);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char* PVtoStringElement(epicsPV pv, int flags, unsigned long index)
|
||||
{
|
||||
double val;
|
||||
long ival;
|
||||
int prec;
|
||||
|
||||
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 = (&pv->CHAR->value)[index];
|
||||
goto printint;
|
||||
case DBF_SHORT:
|
||||
ival = (&pv->SHORT->value)[index];
|
||||
goto printint;
|
||||
case DBF_LONG:
|
||||
ival = (&pv->LONG->value)[index];
|
||||
printint:
|
||||
if (flags & PV_WITHUNITS && pv->units[0])
|
||||
sprintf(pv->stringrep, "%ld %s", ival, pv->units);
|
||||
else
|
||||
sprintf(pv->stringrep, "%ld", ival);
|
||||
break;
|
||||
case DBF_FLOAT:
|
||||
val = (&pv->FLOAT->value)[index];
|
||||
goto printdouble;
|
||||
case DBF_DOUBLE:
|
||||
val = (&pv->DOUBLE->value)[index];
|
||||
printdouble:
|
||||
prec = pv->precision;
|
||||
if (prec > 17) prec = -17;
|
||||
if (prec < -17) prec = -17;
|
||||
if (flags & PV_WITHUNITS && pv->units[0])
|
||||
{
|
||||
if (prec >= 0)
|
||||
sprintf(pv->stringrep, "%.*f %s", prec, val, pv->units);
|
||||
else
|
||||
sprintf(pv->stringrep, "%.*g %s", -prec, val, pv->units);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prec >= 0)
|
||||
sprintf(pv->stringrep, "%.*f", prec, val);
|
||||
else
|
||||
sprintf(pv->stringrep, "%.*g", -prec, val);
|
||||
}
|
||||
break;
|
||||
case DBF_ENUM:
|
||||
ival = (&pv->ENUM->value)[index];
|
||||
if (ival < pv->info_ENUM->no_str)
|
||||
return pv->info_ENUM->strs[ival];
|
||||
sprintf(pv->stringrep, "%ld", ival);
|
||||
break;
|
||||
case DBF_STRING:
|
||||
return (&pv->STRING->value)[index];
|
||||
default:
|
||||
return "<not accessible>";
|
||||
}
|
||||
return pv->stringrep;
|
||||
}
|
||||
|
||||
static void getCallback(struct event_handler_args args)
|
||||
{
|
||||
epicsPV pv = args.usr;
|
||||
|
||||
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;
|
||||
}
|
||||
memcpy(pv->data, args.dbr, dbr_size_n(args.type, args.count));
|
||||
if (pv->STRING->severity > lastEpicsAlarmSev)
|
||||
{
|
||||
pv->STRING->severity = epicsSevNone;
|
||||
}
|
||||
if (pv->STRING->status > lastEpicsAlarmCond)
|
||||
{
|
||||
pv->STRING->status = epicsAlarmNone;
|
||||
}
|
||||
}
|
||||
|
||||
int cagetList(double timeoutSec, epicsPV pv1, ...)
|
||||
{
|
||||
va_list ap;
|
||||
epicsPV pv;
|
||||
unsigned long faults = 0;
|
||||
double wait = 0.001;
|
||||
double connectTimeout = 2.0;
|
||||
|
||||
/* 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 */
|
||||
connectTimeout = PVwaitForConnect(pv, connectTimeout);
|
||||
}
|
||||
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 */
|
||||
ca_poll();
|
||||
while (timeoutSec != 0.0 && pendingGets != 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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
return faults;
|
||||
}
|
||||
|
||||
static void putCallback(struct event_handler_args args)
|
||||
{
|
||||
epicsPV pv = args.usr;
|
||||
|
||||
printf("completing caput %s\n", ca_name(pv->channel));
|
||||
if (pv->status == ECA_IOINPROGRESS)
|
||||
{
|
||||
pendingPuts--;
|
||||
}
|
||||
pv->status = args.status;
|
||||
if (args.status != ECA_NORMAL)
|
||||
{
|
||||
/* Something went wrong. */
|
||||
fprintf(stderr, "putCallback %s: %s\n",
|
||||
ca_name(pv->channel), ca_message(args.status));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int caputDoubleList(double timeoutSec, int flags, epicsPV pv1, double value1, ...)
|
||||
{
|
||||
va_list ap;
|
||||
epicsPV pv;
|
||||
double value;
|
||||
unsigned long faults = 0;
|
||||
double wait = 0.001;
|
||||
double connectTimeout = 2.0;
|
||||
|
||||
/* Prepate put requests */
|
||||
va_start(ap, value1);
|
||||
pv = pv1;
|
||||
value = value1;
|
||||
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 */
|
||||
connectTimeout = PVwaitForConnect(pv, connectTimeout);
|
||||
}
|
||||
if (ca_state(pv->channel) == cs_conn)
|
||||
{
|
||||
pv->status = ca_put_callback(
|
||||
DBR_DOUBLE,
|
||||
pv->channel,
|
||||
&value,
|
||||
putCallback, pv);
|
||||
if (pv->status != ECA_NORMAL)
|
||||
{
|
||||
/* io error */
|
||||
fprintf(stderr, "%s can't do put: %s\n",
|
||||
ca_name(pv->channel), ca_message(pv->status));
|
||||
faults ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pv->status = ECA_IOINPROGRESS;
|
||||
pendingPuts++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pv->status = ECA_DISCONN;
|
||||
faults++;
|
||||
}
|
||||
}
|
||||
pv = va_arg(ap, epicsPV);
|
||||
value = va_arg(ap, double);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (timeoutSec == 0) return faults;
|
||||
|
||||
/* Wait for acknowledge */
|
||||
ca_poll();
|
||||
while (timeoutSec != 0.0 && pendingPuts != 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;
|
||||
}
|
||||
|
||||
/* Check results */
|
||||
if (!pendingPuts) return faults;
|
||||
va_start(ap, value1);
|
||||
pv = pv1;
|
||||
while (pv)
|
||||
{
|
||||
if (pv->status == ECA_IOINPROGRESS)
|
||||
{
|
||||
pv->status = ECA_TIMEOUT;
|
||||
faults++;
|
||||
pendingPuts--;
|
||||
}
|
||||
pv = va_arg(ap, epicsPV);
|
||||
va_arg(ap, double);
|
||||
}
|
||||
va_end(ap);
|
||||
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