diff --git a/devsupApp/src/Makefile b/devsupApp/src/Makefile index f5f5ced..fa328ed 100644 --- a/devsupApp/src/Makefile +++ b/devsupApp/src/Makefile @@ -37,6 +37,8 @@ devsup_SRCS_DEFAULT += devsupMain.cpp devsup_SRCS_vxWorks += -nil- devsup_SRCS += setup.c +devsup_SRCS += dbrec.c +devsup_SRCS += dbfield.c # Add support from base/src/vxWorks if needed #devsup_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary diff --git a/devsupApp/src/dbfield.c b/devsupApp/src/dbfield.c new file mode 100644 index 0000000..5af209a --- /dev/null +++ b/devsupApp/src/dbfield.c @@ -0,0 +1,192 @@ + +/* python has its own ideas about which version to support */ +#undef _POSIX_C_SOURCE +#undef _XOPEN_SOURCE + +#include + +#include +#include +#include +#include + +typedef struct { + PyObject_HEAD + + DBADDR addr; +} pyField; + +static int pyField_Init(pyField *self, PyObject *args, PyObject *kws) +{ + const char *pvname; + + if(PyArg_ParseTuple(args, "s", &pvname)) + return -1; + + if(dbNameToAddr(pvname, &self->addr)) { + PyErr_SetString(PyExc_ValueError, "Record field not found"); + return -1; + } + + if(self->addr.field_type >= DBF_ENUM) { + PyErr_SetString(PyExc_ValueError, "Access to this field type is not supported"); + return -1; + } + return 0; +} + +static PyObject* pyField_name(pyField *self) +{ + return Py_BuildValue("ss", + self->addr.precord->name, + self->addr.pfldDes->name); +} + +static PyObject* pyField_fldinfo(pyField *self) +{ + short dbf=self->addr.field_type; + short fsize=self->addr.field_size; + unsigned long nelm=self->addr.no_elements; + return Py_BuildValue("hhk", dbf, fsize, nelm); +} + +union dbfvalue { + char dv_sval[MAX_STRING_SIZE]; + epicsUInt32 dv_uval; + epicsInt32 dv_ival; + double dv_fval; +}; + +static PyObject* pyField_getval(pyField *self) +{ + union dbfvalue buf; + long nReq = 1; + short reqtype; + + switch(self->addr.field_type) + { + case DBF_CHAR: + case DBF_UCHAR: + case DBF_SHORT: + case DBF_USHORT: + case DBF_ENUM: + case DBF_LONG: + reqtype = DBF_LONG; + break; + case DBF_ULONG: + reqtype = DBF_ULONG; + break; + case DBF_FLOAT: + case DBF_DOUBLE: + reqtype = DBF_DOUBLE; + break; + case DBF_STRING: + reqtype = DBF_STRING; + break; + default: + PyErr_SetString(PyExc_ValueError, "Access to this field type is not supported"); + return NULL; + } + + if(dbGet(&self->addr, reqtype, buf.dv_sval, NULL, &nReq, NULL)) { + PyErr_SetString(PyExc_ValueError, "Error getting field value"); + return NULL; + } + + switch(reqtype) { + case DBF_LONG: + return PyInt_FromLong(buf.dv_ival); + case DBF_ULONG: + return PyInt_FromLong(buf.dv_uval); + case DBF_DOUBLE: + return PyInt_FromLong(buf.dv_fval); + case DBF_STRING: + return PyString_FromString(buf.dv_sval); + default: + Py_RETURN_NONE; + } +} + + +static PyObject* pyField_putval(pyField *self, PyObject* args) +{ + PyObject *val; + + union dbfvalue buf; + short puttype; + + if(!PyArg_ParseTuple(args, "O", &val)) + return NULL; + + if(PyFloat_Check(val)) { + double v = PyFloat_AsDouble(val); + buf.dv_fval = v; + puttype = DBF_DOUBLE; + + } else if(PyInt_Check(val)) { + long v = PyInt_AsLong(val); + if(v>0x7fffffffL) { + buf.dv_uval = v; + puttype = DBF_ULONG; + } else { + buf.dv_ival = v; + puttype = DBF_LONG; + } + + } else if(PyString_Check(val)) { + const char *v = PyString_AsString(val); + strncpy(buf.dv_sval, v, MAX_STRING_SIZE); + buf.dv_sval[MAX_STRING_SIZE-1] = '\0'; + puttype = DBF_STRING; + + } else { + PyErr_SetString(PyExc_TypeError, "Unable to convert put type"); + return NULL; + } + + if(dbPut(&self->addr, puttype, buf.dv_sval, 1)){ + PyErr_SetString(PyExc_ValueError, "Error putting field value"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyMethodDef pyField_methods[] = { + {"name", (PyCFunction)pyField_name, METH_NOARGS, + "Return Names (\"record\",\"field\")"}, + {"fieldinfo", (PyCFunction)pyField_fldinfo, METH_NOARGS, + "Field type info\nReturn (type, size, #elements"}, + {"getval", (PyCFunction)pyField_getval, METH_VARARGS, + "Returns scalar version of field value"}, + {"putval", (PyCFunction)pyField_putval, METH_VARARGS, + "Sets field value from a scalar"}, + {NULL, NULL, 0, NULL} +}; + + +static PyTypeObject pyField_type = { + PyObject_HEAD_INIT(NULL) + 0, + "_dbapi.Field", + sizeof(pyField), +}; + +int pyField_prepare(void) +{ + pyField_type.tp_flags = Py_TPFLAGS_DEFAULT; + pyField_type.tp_methods = pyField_methods; + pyField_type.tp_init = (initproc)pyField_Init; + + pyField_type.tp_new = PyType_GenericNew; + if(PyType_Ready(&pyField_type)<0) + return -1; + return 0; +} + +void pyField_setup(PyObject *module) +{ + PyObject *typeobj=(PyObject*)&pyField_type; + Py_INCREF(typeobj); + PyModule_AddObject(module, "Field", (PyObject*)&pyField_type); +} diff --git a/devsupApp/src/dbrec.c b/devsupApp/src/dbrec.c new file mode 100644 index 0000000..2318475 --- /dev/null +++ b/devsupApp/src/dbrec.c @@ -0,0 +1,161 @@ + +/* python has its own ideas about which version to support */ +#undef _POSIX_C_SOURCE +#undef _XOPEN_SOURCE + +#include + +#include +#include +#include +#include +#include + +typedef struct { + PyObject_HEAD + + DBENTRY entry; +} pyRecord; + +static int pyRecord_Init(pyRecord *self, PyObject *args, PyObject *kws) +{ + const char *recname; + + if(!PyArg_ParseTuple(args, "s", &recname)) + return -1; + + dbInitEntry(pdbbase, &self->entry); + + if(dbFindRecord(&self->entry, recname)){ + PyErr_SetString(PyExc_ValueError, "Record not found"); + return -1; + } + return 0; +} + +static PyObject* pyRecord_name(pyRecord *self) +{ + dbCommon *prec=self->entry.precnode->precord; + return PyString_FromString(prec->name); +} + +static PyObject* pyRecord_info(pyRecord *self, PyObject *args) +{ + const char *name; + PyObject *def = Py_None; + 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_scan(pyRecord *self, PyObject *args, PyObject *kws) +{ + dbCommon *prec = self->entry.precnode->precord; + + static char* names[] = {"sync", NULL}; + PyObject *sync = Py_False; + + if(!PyArg_ParseTupleAndKeywords(args, kws, "|O", names, &sync)) + return NULL; + + if(!PyObject_IsTrue(sync)) { + scanOnce(prec); + } else { + Py_BEGIN_ALLOW_THREADS { + + dbScanLock(prec); + dbProcess(prec); + dbScanUnlock(prec); + + } Py_END_ALLOW_THREADS + } + + Py_RETURN_NONE; +} + +static PyMethodDef pyRecord_methods[] = { + {"name", (PyCFunction)pyRecord_name, METH_NOARGS, + "Return record name string"}, + {"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."}, + {"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." + }, + {NULL, NULL, 0, NULL} +}; + +static PyTypeObject pyRecord_type = { + PyObject_HEAD_INIT(NULL) + 0, + "_dbapi.Record", + sizeof(pyRecord), +}; + + +int pyRecord_prepare(void) +{ + pyRecord_type.tp_flags = Py_TPFLAGS_DEFAULT; + pyRecord_type.tp_methods = pyRecord_methods; + pyRecord_type.tp_init = (initproc)pyRecord_Init; + + pyRecord_type.tp_new = PyType_GenericNew; + if(PyType_Ready(&pyRecord_type)<0) + return -1; + return 0; +} + +void pyRecord_setup(PyObject *module) +{ + PyObject *typeobj=(PyObject*)&pyRecord_type; + Py_INCREF(typeobj); + PyModule_AddObject(module, "Field", (PyObject*)&pyRecord_type); +} diff --git a/devsupApp/src/setup.c b/devsupApp/src/setup.c index db0ae1a..cfca4c6 100644 --- a/devsupApp/src/setup.c +++ b/devsupApp/src/setup.c @@ -1,3 +1,6 @@ +/* Global interpreter setup + */ + /* python has its own ideas about which version to support */ #undef _POSIX_C_SOURCE #undef _XOPEN_SOURCE @@ -6,49 +9,98 @@ #include +#include +#include +#include +#include +#include +#include #include #include -#include -static PyThreadState *main_state; +static PyObject *hooktable; + +typedef struct { + const initHookState state; + const char * const name; +} pystate; + +#define INITST(hook) {initHook ## hook, #hook } +static pystate statenames[] = { + INITST(AtIocBuild), + + INITST(AtBeginning), + INITST(AfterCallbackInit), + INITST(AfterCaLinkInit), + INITST(AfterInitDrvSup), + INITST(AfterInitRecSup), + INITST(AfterInitDevSup), + INITST(AfterInitDatabase), + INITST(AfterFinishDevSup), + INITST(AfterScanInit), + + INITST(AfterInitialProcess), + INITST(AfterCaServerInit), + INITST(AfterIocBuilt), + INITST(AtIocRun), + INITST(AfterDatabaseRunning), + INITST(AfterCaServerRunning), + INITST(AfterIocRunning), + INITST(AtIocPause), + + INITST(AfterCaServerPaused), + INITST(AfterDatabasePaused), + INITST(AfterIocPaused), + {(initHookState)0, NULL} +}; +#undef INITST static void cleanupPy(void *junk) { - PyEval_RestoreThread(main_state); + PyThreadState *state = PyGILState_GetThisThreadState(); + + PyEval_RestoreThread(state); + + /* release extra reference for hooktable */ + Py_DECREF(hooktable); + hooktable = NULL; Py_Finalize(); } /* Initialize the interpreter environment */ +static epicsThreadOnceId setupPyOnceId = EPICS_THREAD_ONCE_INIT; static void setupPyOnce(void *junk) { - Py_Initialize(); + PyThreadState *state; + Py_Initialize(); PyEval_InitThreads(); + state = PyEval_SaveThread(); + epicsAtExit(&cleanupPy, NULL); - - main_state = PyEval_SaveThread(); } -static epicsThreadOnceId setupPyOnceId = EPICS_THREAD_ONCE_INIT; - void evalPy(const char* code) { - PyEval_RestoreThread(main_state); + PyGILState_STATE state; + + state = PyGILState_Ensure(); if(PyRun_SimpleStringFlags(code, NULL)!=0) PyErr_Print(); - main_state = PyEval_SaveThread(); + PyGILState_Release(state); } void evalFilePy(const char* file) { FILE *fp; + PyGILState_STATE state; - PyEval_RestoreThread(main_state); + state = PyGILState_Ensure(); fp = fopen(file, "r"); if(!fp) { @@ -60,7 +112,97 @@ void evalFilePy(const char* file) } /* fp closed by python */ - main_state = PyEval_SaveThread(); + PyGILState_Release(state); +} + +static void pyhook(initHookState state) +{ + PyGILState_STATE gilstate; + + gilstate = PyGILState_Ensure(); + + if(hooktable && PyDict_Check(hooktable)) { + PyObject *next; + PyObject *key = PyInt_FromLong((long)state); + PyObject *list = PyDict_GetItem(hooktable, key); + Py_DECREF(key); + + list = PyObject_GetIter(list); + if(!list) { + fprintf(stderr, "hook sequence not iterable!"); + + } else { + + while((next=PyIter_Next(list))!=NULL) { + PyObject *obj; + if(!PyCallable_Check(next)) + continue; + obj = PyObject_CallFunction(next, "O", key); + Py_DECREF(next); + if(obj) + Py_DECREF(obj); + else { + PyErr_Print(); + PyErr_Clear(); + } + } + if(!PyErr_Occurred()) { + PyErr_Print(); + PyErr_Clear(); + } + + Py_DECREF(list); + } + } + + PyGILState_Release(gilstate); +} + +static const char sitestr[] = EPICS_SITE_VERSION; + +static PyObject *modversion(PyObject *self) +{ + int ver=EPICS_VERSION, rev=EPICS_REVISION, mod=EPICS_MODIFICATION, patch=EPICS_PATCH_LEVEL; + return Py_BuildValue("iiiis", ver, rev, mod, patch, sitestr); +} + +static PyMethodDef devsup_methods[] = { + {"verinfo", (PyCFunction)modversion, METH_NOARGS, + "EPICS Version information\nreturn (MAJOR, MINOR, MOD, PATH, \"site\""}, + {NULL, NULL, 0, NULL} +}; + +int pyField_prepare(void); +void pyField_setup(PyObject *module); + +int pyRecord_prepare(void); +void pyRecord_setup(PyObject *module); + +/* initialize "magic" builtin module */ +static void init_dbapi(void) +{ + PyObject *mod; + pystate *st; + + hooktable = PyDict_New(); + if(!hooktable) + return; + + if(pyField_prepare()) + return; + if(pyRecord_prepare()) + return; + + mod = Py_InitModule("_dbapi", devsup_methods); + + for(st = statenames; st->name; st++) { + PyModule_AddIntConstant(mod, st->name, (long)st->state); + } + Py_INCREF(hooktable); /* an extra ref */ + PyModule_AddObject(mod, "_hooktable", hooktable); + + pyField_setup(mod); + pyRecord_setup(mod); } #include @@ -82,6 +224,8 @@ static void pySetupReg(void) epicsThreadOnce(&setupPyOnceId, &setupPyOnce, NULL); iocshRegister(&codeDef, &codeRun); iocshRegister(&fileDef, &fileRun); + initHookRegister(&pyhook); + init_dbapi(); } #include diff --git a/test.cmd b/test.cmd index a313d33..1289f19 100644 --- a/test.cmd +++ b/test.cmd @@ -1,3 +1,6 @@ #!./bin/linux-x86-debug/devsup dbLoadDatabase("dbd/devsup.dbd") devsup_registerRecordDeviceDriver(pdbbase) + +evalPy "print 1" +evalPy "print 2"