Files
pyDevSup/devsupApp/src/dbrec.c
2013-04-14 16:25:10 -04:00

337 lines
8.5 KiB
C

/* python has its own ideas about which version to support */
#undef _POSIX_C_SOURCE
#undef _XOPEN_SOURCE
#include <Python.h>
#include <epicsVersion.h>
#include <dbCommon.h>
#include <dbAccess.h>
#include <dbStaticLib.h>
#include <recSup.h>
#include <dbScan.h>
#include <recGbl.h>
#include <alarm.h>
#include "pydevsup.h"
typedef struct {
PyObject_HEAD
DBENTRY entry;
int ispyrec;
} pyRecord;
static void pyRecord_dealloc(pyRecord *self)
{
dbFinishEntry(&self->entry);
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject* pyRecord_new(PyTypeObject *type, PyObject *args, PyObject *kws)
{
pyRecord *self;
self = (pyRecord*)type->tp_alloc(type, 0);
if(self) {
dbInitEntry(pdbbase, &self->entry);
}
return (PyObject*)self;
}
static int pyRecord_Init(pyRecord *self, PyObject *args, PyObject *kws)
{
const char *recname;
if(!PyArg_ParseTuple(args, "s", &recname))
return -1;
if(dbFindRecord(&self->entry, recname)){
PyErr_SetString(PyExc_ValueError, "No record by this name");
return -1;
}
self->ispyrec = isPyRecord(self->entry.precnode->precord);
return 0;
}
static PyObject* pyRecord_ispyrec(pyRecord *self)
{
return PyBool_FromLong(self->ispyrec);
}
static PyObject* pyRecord_name(pyRecord *self)
{
dbCommon *prec=self->entry.precnode->precord;
return PyString_FromString(prec->name);
}
static PyObject* pyRecord_rtype(pyRecord *self)
{
dbCommon *prec=self->entry.precnode->precord;
return PyString_FromString(prec->rdes->name);
}
static PyObject* pyRecord_info(pyRecord *self, PyObject *args)
{
const char *name;
PyObject *def = NULL;
DBENTRY entry;
if(!PyArg_ParseTuple(args, "s|O", &name, &def))
return NULL;
dbCopyEntryContents(&self->entry, &entry);
if(dbFindInfo(&entry, name)) {
if(def) {
Py_INCREF(def);
return def;
} else {
PyErr_SetNone(PyExc_KeyError);
return NULL;
}
}
return PyString_FromString(dbGetInfoString(&entry));
}
static PyObject* pyRecord_infos(pyRecord *self)
{
PyObject *dict;
DBENTRY entry;
long status;
dict = PyDict_New();
if(!dict)
return NULL;
dbCopyEntryContents(&self->entry, &entry);
for(status = dbFirstInfo(&entry); status==0; status = dbNextInfo(&entry))
{
PyObject *val = PyString_FromString(dbGetInfoString(&entry));
if(!val)
goto fail;
if(PyDict_SetItemString(dict, dbGetInfoName(&entry), val)<0) {
Py_DECREF(val);
goto fail;
}
}
return dict;
fail:
Py_DECREF(dict);
return NULL;
}
static PyObject* pyRecord_setSevr(pyRecord *self, PyObject *args, PyObject *kws)
{
dbCommon *prec = self->entry.precnode->precord;
static char* names[] = {"sevr", "stat", NULL};
short sevr = INVALID_ALARM, stat=COMM_ALARM;
if(!PyArg_ParseTupleAndKeywords(args, kws, "|hh", names, &sevr, &stat))
return NULL;
if(sevr<firstEpicsAlarmSev || sevr>lastEpicsAlarmSev
|| stat<firstEpicsAlarmCond || stat>lastEpicsAlarmCond)
{
PyErr_Format(PyExc_ValueError, "%s: Can't set alarms %d %d", prec->name, sevr, stat);
return NULL;
}
recGblSetSevr(prec, sevr, stat);
Py_RETURN_NONE;
}
static PyObject* pyRecord_setTime(pyRecord *self, PyObject *args)
{
dbCommon *prec = self->entry.precnode->precord;
long sec, nsec;
if(!PyArg_ParseTuple(args, "ll", &sec, &nsec))
return NULL;
if(prec->tse != epicsTimeEventDeviceTime)
Py_RETURN_NONE;
sec -= POSIX_TIME_AT_EPICS_EPOCH;
if(sec<0 || nsec<0 || nsec>=1000000000) {
PyErr_Format(PyExc_ValueError,"%s: Can't set invalid time %ld:%ld",
prec->name, sec, nsec);
return NULL;
}
prec->time.secPastEpoch = sec;
prec->time.nsec = nsec;
Py_RETURN_NONE;
}
static PyObject* pyRecord_scan(pyRecord *self, PyObject *args, PyObject *kws)
{
dbCommon *prec = self->entry.precnode->precord;
static char* names[] = {"sync", "reason", "force", NULL};
unsigned int force = 0;
PyObject *reason = Py_None;
PyObject *sync = Py_False;
if(!PyArg_ParseTupleAndKeywords(args, kws, "|OOI", names, &sync, &reason, &force))
return NULL;
if(!PyObject_IsTrue(sync)) {
scanOnce(prec);
Py_RETURN_NONE;
} else {
long ret=-1;
int ran=0;
setReasonPyRecord(prec, reason);
Py_BEGIN_ALLOW_THREADS {
dbScanLock(prec);
if(force==1
|| (force==0 && prec->scan==menuScanPassive)
|| (force==2 && prec->scan==menuScanI_O_Intr && canIOScanRecord(prec)))
{
ran = 1;
ret = dbProcess(prec);
}
dbScanUnlock(prec);
} Py_END_ALLOW_THREADS
clearReasonPyRecord(prec);
if(ran)
return PyLong_FromLong(ret);
else {
PyErr_SetNone(PyExc_RuntimeError);
return NULL;
}
}
}
static PyObject *pyRecord_asyncStart(pyRecord *self)
{
dbCommon *prec=self->entry.precnode->precord;
epicsUInt8 pact = prec->pact;
if(!isPyRecord(prec)) {
PyErr_SetString(PyExc_RuntimeError, "Not a Python Device record");
return NULL;
}
prec->pact = 1;
return PyLong_FromLong(pact);
}
static PyObject *pyRecord_asyncFinish(pyRecord *self, PyObject *args, PyObject *kws)
{
long pact, ret;
dbCommon *prec = self->entry.precnode->precord;
static char* names[] = {"reason", NULL};
PyObject *reason = Py_None;
if(!PyArg_ParseTupleAndKeywords(args, kws, "|O", names, &reason))
return NULL;
if(!isPyRecord(prec)) {
PyErr_SetString(PyExc_RuntimeError, "Not a Python Device record");
return NULL;
}
Py_INCREF(self); /* necessary? */
setReasonPyRecord(prec, reason);
Py_BEGIN_ALLOW_THREADS {
rset *rsup = prec->rset;
dbScanLock(prec);
pact = prec->pact;
if(pact) {
ret = (*rsup->process)(prec);
/* Out devsup always clears PACT if initially set */
}
dbScanUnlock(prec);
} Py_END_ALLOW_THREADS
clearReasonPyRecord(prec);
if(!pact) {
PyErr_SetString(PyExc_ValueError, "Python Device record was not active");
return NULL;
}
Py_DECREF(self);
return PyLong_FromLong(ret);
}
static PyMethodDef pyRecord_methods[] = {
{"name", (PyCFunction)pyRecord_name, METH_NOARGS,
"Return record name string"},
{"rtype", (PyCFunction)pyRecord_rtype, METH_NOARGS,
"Return record type name string"},
{"isPyRecord", (PyCFunction)pyRecord_ispyrec, METH_NOARGS,
"Is this record using Python Device."},
{"info", (PyCFunction)pyRecord_info, METH_VARARGS,
"Lookup info name\ninfo(name, def=None)"},
{"infos", (PyCFunction)pyRecord_infos, METH_NOARGS,
"Return a dictionary of all infos for this record."},
{"setSevr", (PyCFunction)pyRecord_setSevr, METH_VARARGS|METH_KEYWORDS,
"Set alarm new alarm severity/status. Record must be locked!"},
{"setTime", (PyCFunction)pyRecord_setTime, METH_VARARGS,
"Set record timestamp if TSE==-2. Record must be locked!"},
{"scan", (PyCFunction)pyRecord_scan, METH_VARARGS|METH_KEYWORDS,
"scan(sync=False)\nScan this record. If sync is False then"
"a scan request is queued. If sync is True then the record"
"is scannined immidately on the current thread."},
{"asyncStart", (PyCFunction)pyRecord_asyncStart, METH_NOARGS,
"Begin an asynchronous action. Record must be locked!"},
{"asyncFinish", (PyCFunction)pyRecord_asyncFinish, METH_VARARGS|METH_KEYWORDS,
"Complete an asynchronous action. Record must *not* be locked!"},
{NULL, NULL, 0, NULL}
};
static PyTypeObject pyRecord_type = {
#if PY_MAJOR_VERSION >= 3
PyVarObject_HEAD_INIT(NULL, 0)
#else
PyObject_HEAD_INIT(NULL)
0,
#endif
"_dbapi._Record",
sizeof(pyRecord),
};
int pyRecord_prepare(PyObject *module)
{
PyObject *typeobj=(PyObject*)&pyRecord_type;
pyRecord_type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE;
pyRecord_type.tp_methods = pyRecord_methods;
pyRecord_type.tp_new = (newfunc)pyRecord_new;
pyRecord_type.tp_dealloc = (destructor)pyRecord_dealloc;
pyRecord_type.tp_init = (initproc)pyRecord_Init;
if(PyType_Ready(&pyRecord_type)<0)
return -1;
Py_INCREF(typeobj);
if(PyModule_AddObject(module, "_Record", typeobj)) {
Py_DECREF(typeobj);
return -1;
}
return 0;
}