Files
caClientLessons/caLesson6/caClientWrapperC/epicsPV.c
T

573 lines
15 KiB
C

#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;
}
*/