Files
pyDevSup/devsupApp/src/dbapi.c

418 lines
11 KiB
C

/* python has its own ideas about which version to support */
#undef _POSIX_C_SOURCE
#undef _XOPEN_SOURCE
#include <Python.h>
#ifdef HAVE_NUMPY
#include <numpy/ndarrayobject.h>
#endif
#include <stdio.h>
#include <epicsVersion.h>
#include <errlog.h>
#include <errMdef.h>
#include <dbCommon.h>
#include <dbAccess.h>
#include <dbStaticLib.h>
#include <dbScan.h>
#include <initHooks.h>
#include <epicsThread.h>
#include <epicsExit.h>
#include <alarm.h>
#include <iocsh.h>
#include <iocInit.h>
#include "pydevsup.h"
initHookState pyInitLastState;
/* extern int pyDevSupCommon_registerRecordDeviceDriver(DBBASE *pbase); */
typedef struct {
const initHookState state;
const char * const name;
} pystate;
epicsThreadPrivateId pyDevReasonID;
#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)9999, "AtIocExit"},
{(initHookState)0, NULL}
};
#undef INITST
void py(const char* code)
{
PyGILState_STATE state;
if(!code)
return;
state = PyGILState_Ensure();
if(PyRun_SimpleStringFlags(code, NULL)!=0)
PyErr_Print();
PyGILState_Release(state);
}
void pyfile(const char* file)
{
FILE *fp;
PyGILState_STATE state;
if(!file)
return;
state = PyGILState_Ensure();
fp = fopen(file, "r");
if(!fp) {
fprintf(stderr, "Failed to open: %s\n", file);
perror("open");
} else {
if(PyRun_SimpleFileExFlags(fp, file, 1, NULL)!=0)
PyErr_Print();
}
/* fp closed by python */
PyGILState_Release(state);
}
static const iocshArg argCode = {"python code", iocshArgString};
static const iocshArg argFile = {"file", iocshArgString};
static const iocshArg* const codeArgs[] = {&argCode};
static const iocshArg* const fileArgs[] = {&argFile};
static const iocshFuncDef codeDef = {"py", 1, codeArgs};
static const iocshFuncDef fileDef = {"pyfile", 1, fileArgs};
static void codeRun(const iocshArgBuf *args){py(args[0].sval);}
static void fileRun(const iocshArgBuf *args){pyfile(args[0].sval);}
initHookState pyInitLastState = (initHookState)-1;
static void pyhook(initHookState state)
{
static int madenoise = 0;
PyGILState_STATE gilstate;
PyObject *mod, *ret;
/* ignore deprecated init hooks */
if(state==initHookAfterInterruptAccept || state==initHookAtEnd)
return;
gilstate = PyGILState_Ensure();
pyInitLastState = state;
mod = PyImport_ImportModule("devsup.hooks");
if(!mod) {
if(!madenoise)
fprintf(stderr, "Error: Couldn't import devsup.hooks! Python module initHooks can not be run!\n");
madenoise=1;
goto fail;
}
ret = PyObject_CallMethod(mod, "_runhook", "l", (long)state);
Py_DECREF(mod);
if(PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
Py_XDECREF(ret);
fail:
PyGILState_Release(gilstate);
}
static
PyObject* py_announce(PyObject *unused, PyObject *args, PyObject *kws)
{
static char* names[] = {"state", NULL};
int state;
if(!PyArg_ParseTupleAndKeywords(args, kws, "i", names, &state))
return NULL;
Py_BEGIN_ALLOW_THREADS {
initHookAnnounce((initHookState)state);
} Py_END_ALLOW_THREADS
Py_RETURN_NONE;
}
static
PyObject *py_iocsh(PyObject *unused, PyObject *args, PyObject *kws)
{
int ret;
static char* names[] = {"script", "cmd", NULL};
char *script=NULL, *cmd=NULL;
if(!PyArg_ParseTupleAndKeywords(args, kws, "|ss", names, &script, &cmd))
return NULL;
if(!(!script ^ !cmd)) {
PyErr_SetString(PyExc_ValueError, "iocsh requires a script file name or command string");
return NULL;
}
Py_BEGIN_ALLOW_THREADS {
if(script)
ret = iocsh(script);
else
ret = iocshCmd(cmd);
} Py_END_ALLOW_THREADS
return PyInt_FromLong(ret);
}
static
PyObject *py_dbReadDatabase(PyObject *unused, PyObject *args, PyObject *kws)
{
long status;
static char* names[] = {"name", "fp", "path", "sub", NULL};
char *fname=NULL, *path=NULL, *sub=NULL;
int fd=-1;
if(!PyArg_ParseTupleAndKeywords(args, kws, "|siss", names, &fname, &fd, &path, &sub))
return NULL;
if(!((!fname) ^ (fd<0))) {
PyErr_SetString(PyExc_ValueError, "dbReadDatabase requires a file name or descriptor");
return NULL;
}
Py_BEGIN_ALLOW_THREADS {
if(fname) {
status = dbReadDatabase(&pdbbase, fname, path, sub);
} else {
FILE *ff = fdopen(fd, "r");
status = dbReadDatabaseFP(&pdbbase, ff, path, sub);
// dbReadDatabaseFP() has called fclose()
}
} Py_END_ALLOW_THREADS
if(status) {
char buf[30];
errSymLookup(status, buf, sizeof(buf));
PyErr_SetString(PyExc_RuntimeError, buf);
return NULL;
}
Py_RETURN_NONE;
}
static
PyObject *py_iocInit(PyObject *unused, PyObject *args, PyObject *kws)
{
static char* names[] = {"isolate", NULL};
PyObject *pyisolate = Py_True;
int isolate, ret;
if(!PyArg_ParseTupleAndKeywords(args, kws, "|O", names, &pyisolate))
return NULL;
isolate = PyObject_IsTrue(pyisolate);
Py_BEGIN_ALLOW_THREADS {
if(isolate) {
#if EPICS_VERSION_INT<VERSION_INT(3,15,0,0)
return PyErr_Format(PyExc_RuntimeError, "iocInit(isolate=True) requires Base>=3.15");
#else
ret = iocBuildIsolated();
#endif
} else {
ret = iocBuild();
}
if(!ret)
ret = iocRun();
} Py_END_ALLOW_THREADS
if(ret)
return PyErr_Format(PyExc_RuntimeError, "Error %d", ret);
Py_RETURN_NONE;
}
static
PyObject *py_pyDevSupCommon(PyObject *unused)
{
/*
Py_BEGIN_ALLOW_THREADS {
pyDevSupCommon_registerRecordDeviceDriver(pdbbase);
} Py_END_ALLOW_THREADS
*/
Py_RETURN_NONE;
}
static struct PyMethodDef dbapimethod[] = {
{"initHookAnnounce", (PyCFunction)py_announce, METH_VARARGS|METH_KEYWORDS,
"initHookAnnounce(state)\n"},
{"iocsh", (PyCFunction)py_iocsh, METH_VARARGS|METH_KEYWORDS,
"Execute IOC shell script or command"},
{"dbReadDatabase", (PyCFunction)py_dbReadDatabase, METH_VARARGS|METH_KEYWORDS,
"Load EPICS database file"},
{"iocInit", (PyCFunction)py_iocInit, METH_NOARGS,
"Initialize IOC"},
{"_dbd_setup", (PyCFunction)pyDBD_setup, METH_NOARGS, ""},
{"_dbd_rrd_base", (PyCFunction)py_pyDevSupCommon, METH_NOARGS, ""},
{"_dbd_cleanup", (PyCFunction)pyDBD_cleanup, METH_NOARGS, ""},
{NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef dbapimodule = {
PyModuleDef_HEAD_INIT,
"devsup._dbapi",
NULL,
-1,
&dbapimethod
};
#endif
#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC PyInit__dbapi(void)
#else
PyMODINIT_FUNC init_dbapi(void)
#endif
{
PyObject *mod = NULL, *hookdict, *vertup;
pystate *st;
pyDevReasonID = epicsThreadPrivateCreate();
iocshRegister(&codeDef, &codeRun);
iocshRegister(&fileDef, &fileRun);
initHookRegister(&pyhook);
import_array();
#if PY_MAJOR_VERSION >= 3
mod = PyModule_Create(&dbapimodule);
#else
mod = Py_InitModule("devsup._dbapi", dbapimethod);
#endif
if(!mod)
goto fail;
hookdict = PyDict_New();
if(!hookdict)
goto fail;
PyModule_AddObject(mod, "_hooks", hookdict);
for(st = statenames; st->name; st++) {
PyObject *ent = PyInt_FromLong((long)st->state);
if(!ent) goto fail;
if(PyDict_SetItemString(hookdict, st->name, ent)) {
Py_DECREF(ent);
goto fail;
}
}
PyModule_AddIntMacro(mod, NO_ALARM);
PyModule_AddIntMacro(mod, MINOR_ALARM);
PyModule_AddIntMacro(mod, MAJOR_ALARM);
PyModule_AddIntMacro(mod, INVALID_ALARM);
PyModule_AddIntMacro(mod, READ_ALARM);
PyModule_AddIntMacro(mod, WRITE_ALARM);
PyModule_AddIntMacro(mod, HIHI_ALARM);
PyModule_AddIntMacro(mod, HIGH_ALARM);
PyModule_AddIntMacro(mod, LOLO_ALARM);
PyModule_AddIntMacro(mod, LOW_ALARM);
PyModule_AddIntMacro(mod, STATE_ALARM);
PyModule_AddIntMacro(mod, COS_ALARM);
PyModule_AddIntMacro(mod, COMM_ALARM);
PyModule_AddIntMacro(mod, TIMEOUT_ALARM);
PyModule_AddIntMacro(mod, HW_LIMIT_ALARM);
PyModule_AddIntMacro(mod, CALC_ALARM);
PyModule_AddIntMacro(mod, SCAN_ALARM);
PyModule_AddIntMacro(mod, LINK_ALARM);
PyModule_AddIntMacro(mod, SOFT_ALARM);
PyModule_AddIntMacro(mod, BAD_SUB_ALARM);
PyModule_AddIntMacro(mod, UDF_ALARM);
PyModule_AddIntMacro(mod, DISABLE_ALARM);
PyModule_AddIntMacro(mod, SIMM_ALARM);
PyModule_AddIntMacro(mod, READ_ACCESS_ALARM);
PyModule_AddIntMacro(mod, WRITE_ACCESS_ALARM);
/* standard macros from epicsVersion.h */
PyModule_AddStringMacro(mod, EPICS_VERSION_STRING);
#ifdef EPICS_DEV_SNAPSHOT
PyModule_AddStringMacro(mod, EPICS_DEV_SNAPSHOT);
#endif
PyModule_AddStringMacro(mod, EPICS_SITE_VERSION);
PyModule_AddIntMacro(mod, EPICS_VERSION);
PyModule_AddIntMacro(mod, EPICS_REVISION);
PyModule_AddIntMacro(mod, EPICS_PATCH_LEVEL);
PyModule_AddIntMacro(mod, EPICS_MODIFICATION);
/* additional build time info */
PyModule_AddStringMacro(mod, XEPICS_ARCH);
PyModule_AddStringMacro(mod, XPYDEV_BASE);
PyModule_AddStringMacro(mod, XEPICS_BASE);
vertup = Py_BuildValue("(iiiiss)",
(int)EPICS_VERSION,
(int)EPICS_REVISION,
(int)EPICS_MODIFICATION,
(int)EPICS_PATCH_LEVEL,
EPICS_SITE_VERSION,
#ifdef EPICS_DEV_SNAPSHOT
EPICS_DEV_SNAPSHOT);
#else
"");
#endif
if(vertup)
PyModule_AddObject(mod, "epicsver", vertup);
/* pyDevSup version */
vertup = Py_BuildValue("(ii)", 0, 2);
if(vertup)
PyModule_AddObject(mod, "pydevver", vertup);
if(pyField_prepare(mod))
goto fail;
if(pyRecord_prepare(mod))
goto fail;
if(pyUTest_prepare(mod))
goto fail;
MODINIT_RET(mod);
fail:
fprintf(stderr, "Failed to initialize builtin _dbapi module!\n");
Py_XDECREF(mod);
MODINIT_RET(NULL);
}