diff --git a/iocBoot/iocpydev/st.cmd b/iocBoot/iocpydev/st.cmd index 6367dbb..52265ee 100755 --- a/iocBoot/iocpydev/st.cmd +++ b/iocBoot/iocpydev/st.cmd @@ -13,7 +13,7 @@ pydevioc_registerRecordDeviceDriver pdbbase ## Load record instances dbLoadRecords("${TOP}/db/pydevtest.db") -dbLoadRecords("${TOP}/db/pyrectest.db") +dbLoadRecords("${TOP}/db/pycalcrectest.db") cd ${TOP}/iocBoot/${IOC} diff --git a/PyRecord.md b/pycalcRecord.md similarity index 89% rename from PyRecord.md rename to pycalcRecord.md index 15cb5a0..bfda11f 100644 --- a/PyRecord.md +++ b/pycalcRecord.md @@ -1,6 +1,6 @@ -# PyRecord +# pycalcRecord -PyRecord is a custom record that allows passing multiple parameters to +pycalcRecord is a custom record that allows passing multiple parameters to Python code and returning result of executed code as record value. Most common use case is to invoke Python function and using it's return value. But any Python expression can be evaluated the same way, including @@ -11,12 +11,12 @@ pass a single parameter to Python code at a time. ## Parameter fields ### Processing -PyRecord has the standard fields for specifying under what +pycalcRecord has the standard fields for specifying under what curcuimstances the record will process. Refer to EPICS record commond [Scan Fields](https://wiki-ext.aps.anl.gov/epics/index.php?title=RRM_3-14_dbCommon#Scan_Fields) documentation for their description, ie. SCAN, PINI and other processing fields. When INPx field contains a link and uses CP or CPP -subscription, the PyRecord will process when link's monitor is posted +subscription, the pycalcRecord will process when link's monitor is posted according to [Channel Access Links](https://wiki-ext.aps.anl.gov/epics/index.php?title=RRM_3-14_Concepts#Channel_Access_Links) specification. ### Input fields @@ -54,14 +54,14 @@ transfered to VAL field and the monitor is posted. In case Python code can not be executed or throws an exception, the record alarm is set to epicsAlarmCalc and severity is epicsSevInvalid. -### Including PyRecord support in the IOC +### Including pycalcRecord support in the IOC In addition to linking against pydev library as explained in [README.md](README.md#adding-pydevice-support-to-ioc), one must also include -pyRecord.dbd in the IOC as +pycalcRecord.dbd in the IOC as ``` -_DBD += pyRecord.dbd +_DBD += pycalcRecord.dbd ``` ## Examples @@ -69,7 +69,7 @@ pyRecord.dbd in the IOC as ### Simple expression ``` -record(py, "PyRecTest:MathExpr") { +record(py, "PyCalcTest:MathExpr") { field(INPA, "17") field(INPB, "3") field(CALC, "%A%*%B%") @@ -86,7 +86,7 @@ FTVL field. ### Adaptive parameter and return value types ``` -record(py, "PyRecTest:AdaptiveTypes") { +record(py, "PyCalcTest:AdaptiveTypes") { field(INPA, "Test:Input1 CP") field(INPB, "Test:Input2 CP") field(CALC, "pow(max([%A%, %B%]), 2)") @@ -104,7 +104,7 @@ float() functions surrounding the expression. ### Trigger record alarm ``` -record(py, "PyRecTest:InvalidAlarm") { +record(py, "PyCalcTest:InvalidAlarm") { field(CALC, "unknown_function()") } ``` @@ -112,4 +112,4 @@ record(py, "PyRecTest:InvalidAlarm") { When Python code fails to execute, the records' alarm is set to CALC and the severity is set to INVALID. Python code can fail due to a syntax error, a module or a function not defined, or a Python exception is raised. -Set TPRO field to 1 to print error details to the IOC console. \ No newline at end of file +Set TPRO field to 1 to print error details to the IOC console. diff --git a/src/Makefile b/src/Makefile index 59ef3b9..23775e3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,9 +11,9 @@ CXXFLAGS += -g -ggdb -O0 LIBRARY_IOC += pydev DBD += pydev.dbd -DBDINC += pyRecord +DBDINC += pycalcRecord -pydev_DBD += pyRecord.dbd +pydev_DBD += pycalcRecord.dbd pydev_SRCS += asyncexec.cpp pydev_SRCS += epicsdevice.cpp @@ -30,7 +30,7 @@ pydev_SRCS += pydev_longout.cpp pydev_SRCS += pydev_stringin.cpp pydev_SRCS += pydev_stringout.cpp pydev_SRCS += pydev_waveform.cpp -pydev_SRCS += pyRecord.cpp +pydev_SRCS += pycalcRecord.cpp # 3.15 and above support lsi, lso and printf records ifdef BASE_3_15 diff --git a/src/pyRecord.cpp.bck b/src/pyRecord.cpp.bck deleted file mode 100644 index 2bed06e..0000000 --- a/src/pyRecord.cpp.bck +++ /dev/null @@ -1,287 +0,0 @@ -/*************************************************************************\ -* PyDevice is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ - -/* Record Support Routines for pyRecord */ -/* - * Author: Klemen Vodopivec - * Date: 12-18-2021 - */ - -#include "alarm.h" -// #include "alarmString.h" -#include "callback.h" -// #include "cantProceed.h" -#include "dbAccess.h" -// #include "dbEvent.h" -// #include "devSup.h" -// #include "epicsTime.h" -#include "epicsVersion.h" -#include "errlog.h" -// #include "menuYesNo.h" -#include "recSup.h" -#include "recGbl.h" - -#include - -#include "asyncexec.h" -#include "pywrapper.h" -#include "util.h" - -#define GEN_SIZE_OFFSET -#include "pyRecord.h" -#undef GEN_SIZE_OFFSET -#include "epicsExport.h" - -#ifdef VERSION_INT -# if EPICS_VERSION_INT < VERSION_INT(3,16,0,2) -# define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT) -# define RECSUPFUN_CAST (RECSUPFUN) -# else -# define RECSUPFUN_CAST -# endif -#else -# define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT) -# define RECSUPFUN_CAST (RECSUPFUN) -#endif - -static long initRecord(dbCommon *, int); -static long processRecord(dbCommon *); -static long updateRecordField(DBADDR *addr, int after); -static long convertDbAddr(DBADDR *addr); -static long getArrayInfo(DBADDR *paddr, long *no_elements, long *offset); -//static void checkLinksCallback(epicsCallback *arg); -//static void checkLinks(pyRecord *rec); -static long fetchValues(pyRecord *rec); - -struct PyRecordContext { - CALLBACK callback; - IOSCANPVT scan; - int processCbStatus; -}; - -rset pyRSET = { - .number = RSETNUMBER, - .report = NULL, - .init = NULL, - .init_record = RECSUPFUN_CAST initRecord, - .process = RECSUPFUN_CAST processRecord, -// .special = RECSUPFUN_CAST updateRecordField, - .special = NULL, - .get_value = NULL, - .cvt_dbaddr = RECSUPFUN_CAST convertDbAddr, - .get_array_info = RECSUPFUN_CAST getArrayInfo, - .put_array_info = NULL, - .get_units = NULL, - .get_precision = NULL, - .get_enum_str = NULL, - .get_enum_strs = NULL, - .put_enum_str = NULL, - .get_graphic_double = NULL, - .get_control_double = NULL, - .get_alarm_double = NULL, -}; -epicsExportAddress(rset, pyRSET); - -static long initRecord(dbCommon *common, int pass) -{ - auto rec = reinterpret_cast(common); - - if (pass == 0) { - // Allocate record context - auto buffer = callocMustSucceed(1, sizeof(struct PyRecordContext), "pyRecord::initRecord"); - rec->ctx = new (buffer) PyRecordContext; - - // Allocate value fields - for (int i = 0; i < PYREC_NARGS; i++) { - auto ft = &rec->fta + i; - auto no = &rec->noa + i; - auto val = &rec->a + i; - auto ne = &rec->nea + i; - - if (*ft >= DBF_ENUM) { - *ft = DBF_CHAR; - } - if (*no == 0) { - *no = 1; - } - - auto size = dbValueSize(*ft); - *val = callocMustSucceed(*no, size, "pyRecord::initRecord"); - *ne = *no; - } - - // Allocate output VAL field - if (rec->ftvl >= DBF_ENUM) { - rec->ftvl = DBF_CHAR; - } - auto size = dbValueSize(rec->ftvl); - long n = (rec->ftvl == DBF_STRING ? 127 : 1); - rec->val = callocMustSucceed(n, size, "pyRecord::initRecord"); - return 0; - } - - // Initialize input links - for (int i = 0; i < PYREC_NARGS; i++) { - auto inp = &rec->inpa + i; - auto dbr = &rec->fta + i; - auto val = &rec->a + i; - long no = *(&rec->noa + i); - - dbLoadLinkArray(inp, *dbr, *val, &no); - if (no > 0) { - auto ne = &rec->nea + i; - *ne = no; - } - } - - return 0; -} - -static void processRecordCb(pyRecord* rec) -{ - auto fields = Util::getReplacables(rec->exec); - for (auto& keyval: fields) { - if (keyval.first == "%NAME%") keyval.second = rec->name; - else { - for (auto i = 0; i < PYREC_NARGS; i++) { - std::string field = "%" + std::string(1,'A'+i) + "%"; - if (keyval.first == field) { - auto val = &rec->a + i; - auto ft = &rec->fta + i; - auto no = &rec->noa + i; - if (*no == 1) { - if (*ft == DBR_CHAR) keyval.second = std::to_string((reinterpret_cast< epicsInt8*>(*val))[0]); - else if (*ft == DBR_UCHAR) keyval.second = std::to_string((reinterpret_cast< epicsUInt8*>(*val))[0]); - else if (*ft == DBR_SHORT) keyval.second = std::to_string((reinterpret_cast< epicsInt16*>(*val))[0]); - else if (*ft == DBR_USHORT) keyval.second = std::to_string((reinterpret_cast< epicsUInt16*>(*val))[0]); - else if (*ft == DBR_LONG) keyval.second = std::to_string((reinterpret_cast< epicsInt32*>(*val))[0]); - else if (*ft == DBR_ULONG) keyval.second = std::to_string((reinterpret_cast< epicsUInt32*>(*val))[0]); - else if (*ft == DBR_INT64) keyval.second = std::to_string((reinterpret_cast< epicsInt64*>(*val))[0]); - else if (*ft == DBR_UINT64) keyval.second = std::to_string((reinterpret_cast< epicsUInt64*>(*val))[0]); - else if (*ft == DBR_FLOAT) keyval.second = std::to_string((reinterpret_cast(*val))[0]); - else if (*ft == DBR_DOUBLE) keyval.second = std::to_string((reinterpret_cast(*val))[0]); - else if (*ft == DBR_STRING) keyval.second = ((char*)val)[0]; - } - } - } - } - } - std::string code = Util::replace(rec->exec, fields); - - try { - if (rec->ftvl == DBR_STRING) { - std::string ret; - PyWrapper::exec(code, (rec->tpro == 1), ret); - } else if (rec->ftvl == DBR_DOUBLE || rec->ftvl == DBR_FLOAT) { - double *ret = reinterpret_cast(rec->val); - PyWrapper::exec(code, (rec->tpro == 1), ret); - } else { - int32_t *ret = reinterpret_cast(rec->val); - PyWrapper::exec(code, (rec->tpro == 1), ret); - } - rec->ctx->processCbStatus = 0; - } catch (...) { - rec->ctx->processCbStatus = -1; - } - callbackRequestProcessCallback(&rec->ctx->callback, rec->prio, rec); -} - -static long processRecord(dbCommon *common) -{ - auto rec = reinterpret_cast(common); - - if (rec->pact == 0) { - rec->pact = 1; - if (fetchValues(rec) != 0) { - recGblSetSevr(rec, epicsAlarmCalc, epicsSevInvalid); - rec->pact = 0; - return -1; - } - - auto scheduled = AsyncExec::schedule([rec]() { - processRecordCb(rec); - }); - return (scheduled ? 0 : -1); - } - - if (rec->ctx->processCbStatus == -1) { - recGblSetSevr(rec, epicsAlarmCalc, epicsSevInvalid); - rec->pact = 0; - return -1; - } - - auto monitor_mask = recGblResetAlarms(rec); - monitor_mask |= DBE_VALUE; - db_post_events(rec, &rec->val, monitor_mask); - - recGblGetTimeStamp(rec); - recGblFwdLink(rec); - rec->pact = 0; - - return 0; -} - -static long fetchValues(pyRecord *rec) -{ - for (auto i = 0; i < PYREC_NARGS; i++) { - auto inp = &rec->inpa + i; - auto fta = &rec->fta + i; - auto no = &rec->noa + i; - auto ne = &rec->nea + i; - auto val = &rec->a + i; - long n = *no; - if (!dbLinkIsConstant(inp)) { - auto status = dbGetLink(inp, *fta, *val, 0, &n); - if (status) { - return status; - } - } - *ne = n; - } - return 0; -} - -static long convertDbAddr(DBADDR *paddr) -{ - auto rec = reinterpret_cast(paddr->precord); - int field = dbGetFieldIndex(paddr); - - if (field >= pyRecordA && field < (pyRecordA + PYREC_NARGS)) { - int offset = field - pyRecordA; - - paddr->pfield = *(&rec->a + offset); - paddr->no_elements = *(&rec->noa + offset); - paddr->field_type = *(&rec->fta + offset); - } else if (field == pyRecordVAL) { - paddr->pfield = rec->val; - paddr->no_elements = (rec->ftvl == DBR_STRING ? 127 : 1); - paddr->field_type = rec->ftvl; - } else { - errlogPrintf("pyRecord::convertDbAddr called for %s.%s\n", rec->name, paddr->pfldDes->name); - return 0; - } - paddr->dbr_field_type = paddr->field_type; - paddr->field_size = dbValueSize(paddr->field_type); - return 0; -} - -static long getArrayInfo(DBADDR *paddr, long *no_elements, long *offset) -{ - auto rec = reinterpret_cast(paddr->precord); - int field = dbGetFieldIndex(paddr); - - if (field >= pyRecordA && field < (pyRecordA + PYREC_NARGS)) { - int off = field - pyRecordA; - *no_elements = *(&rec->nea + off); - } else if (field == pyRecordVAL) { - *no_elements = (rec->ftvl == DBR_STRING ? 127 : 1); - } else { - errlogPrintf("pyRecord::getArrayInfo called for %s.%s\n", rec->name, paddr->pfldDes->name); - } - *offset = 0; -fprintf(stderr, "no_elements=%d, offset=%d\n", *no_elements, *offset); - - return 0; -} \ No newline at end of file diff --git a/src/pyRecord.cpp b/src/pycalcRecord.cpp similarity index 83% rename from src/pyRecord.cpp rename to src/pycalcRecord.cpp index 56ec5d5..43995ee 100644 --- a/src/pyRecord.cpp +++ b/src/pycalcRecord.cpp @@ -3,7 +3,7 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* Record Support Routines for pyRecord */ +/* Record Support Routines for pycalcRecord */ /* * Author: Klemen Vodopivec * Date: 12-18-2021 @@ -28,7 +28,7 @@ #include "util.h" #define GEN_SIZE_OFFSET -#include "pyRecord.h" +#include "pycalcRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" @@ -50,14 +50,14 @@ static long processRecord(dbCommon *); //static long updateRecordField(DBADDR *addr, int after); static long convertDbAddr(DBADDR *addr); static long getArrayInfo(DBADDR *paddr, long *no_elements, long *offset); -static long fetchValues(pyRecord *rec); +static long fetchValues(pycalcRecord *rec); -struct PyRecordContext { +struct PyCalcRecordContext { CALLBACK callback; int processCbStatus; }; -rset pyRSET = { +rset pycalcRSET = { .number = RSETNUMBER, .report = NULL, .init = NULL, @@ -78,19 +78,19 @@ rset pyRSET = { .get_control_double = NULL, .get_alarm_double = NULL, }; -epicsExportAddress(rset, pyRSET); +epicsExportAddress(rset, pycalcRSET); static long initRecord(dbCommon *common, int pass) { - auto rec = reinterpret_cast(common); + auto rec = reinterpret_cast(common); if (pass == 0) { // Allocate record context - auto buffer = callocMustSucceed(1, sizeof(struct PyRecordContext), "pyRecord::initRecord"); - rec->ctx = new (buffer) PyRecordContext; + auto buffer = callocMustSucceed(1, sizeof(struct PyCalcRecordContext), "pycalcRecord::initRecord"); + rec->ctx = new (buffer) PyCalcRecordContext; // Allocate value fields - for (int i = 0; i < PYREC_NARGS; i++) { + for (int i = 0; i < PYCALCREC_NARGS; i++) { auto ft = &rec->fta + i; auto val = &rec->a + i; auto siz = &rec->siza + i; @@ -100,17 +100,17 @@ static long initRecord(dbCommon *common, int pass) } *siz = dbValueSize(*ft); - *val = callocMustSucceed(1, *siz, "pyRecord::initRecord"); + *val = callocMustSucceed(1, *siz, "pycalcRecord::initRecord"); } // Allocate output VAL field for longest possible value - rec->val = callocMustSucceed(1, dbValueSize(DBR_STRING), "pyRecord::initRecord"); + rec->val = callocMustSucceed(1, dbValueSize(DBR_STRING), "pycalcRecord::initRecord"); reinterpret_cast(rec->val)[0] = 0; return 0; } // Initialize input links - for (int i = 0; i < PYREC_NARGS; i++) { + for (int i = 0; i < PYCALCREC_NARGS; i++) { auto inp = &rec->inpa + i; auto ft = &rec->fta + i; auto val = &rec->a + i; @@ -124,13 +124,13 @@ static long initRecord(dbCommon *common, int pass) return 0; } -static void processRecordCb(pyRecord* rec) +static void processRecordCb(pycalcRecord* rec) { auto fields = Util::getFields(rec->calc); for (auto& keyval: fields) { if (keyval.first == "NAME") keyval.second = rec->name; else { - for (auto i = 0; i < PYREC_NARGS; i++) { + for (auto i = 0; i < PYCALCREC_NARGS; i++) { std::string field = std::string(1,'A'+i); if (keyval.first == field) { auto val = &rec->a + i; @@ -189,7 +189,7 @@ static void processRecordCb(pyRecord* rec) static long processRecord(dbCommon *common) { - auto rec = reinterpret_cast(common); + auto rec = reinterpret_cast(common); if (rec->pact == 0) { rec->pact = 1; @@ -225,9 +225,9 @@ static long processRecord(dbCommon *common) return 0; } -static long fetchValues(pyRecord *rec) +static long fetchValues(pycalcRecord *rec) { - for (auto i = 0; i < PYREC_NARGS; i++) { + for (auto i = 0; i < PYCALCREC_NARGS; i++) { auto inp = &rec->inpa + i; auto ft = &rec->fta + i; auto val = &rec->a + i; @@ -241,7 +241,7 @@ static long fetchValues(pyRecord *rec) if (*siz < dbValueSize(ftype)) { free(*val); *siz = dbValueSize(ftype); - *val = callocMustSucceed(1, *siz, "pyRecord::initRecord"); + *val = callocMustSucceed(1, *siz, "pycalcRecord::initRecord"); } *ft = ftype; @@ -254,20 +254,20 @@ static long fetchValues(pyRecord *rec) static long convertDbAddr(DBADDR *paddr) { - auto rec = reinterpret_cast(paddr->precord); + auto rec = reinterpret_cast(paddr->precord); int field = dbGetFieldIndex(paddr); - if (field >= pyRecordA && field < (pyRecordA + PYREC_NARGS)) { - int off = field - pyRecordA; + if (field >= pycalcRecordA && field < (pycalcRecordA + PYCALCREC_NARGS)) { + int off = field - pycalcRecordA; paddr->pfield = *(&rec->a + off); paddr->no_elements = 1; paddr->field_type = *(&rec->fta + off); - } else if (field == pyRecordVAL) { + } else if (field == pycalcRecordVAL) { paddr->pfield = rec->val; paddr->no_elements = 1; paddr->field_type = rec->ftvl; } else { - errlogPrintf("pyRecord::convertDbAddr called for %s.%s\n", rec->name, paddr->pfldDes->name); + errlogPrintf("pycalcRecord::convertDbAddr called for %s.%s\n", rec->name, paddr->pfldDes->name); return 0; } paddr->dbr_field_type = paddr->field_type; @@ -277,15 +277,15 @@ static long convertDbAddr(DBADDR *paddr) static long getArrayInfo(DBADDR *paddr, long *no_elements, long *offset) { - auto rec = reinterpret_cast(paddr->precord); + auto rec = reinterpret_cast(paddr->precord); int field = dbGetFieldIndex(paddr); - if (field >= pyRecordA && field < (pyRecordA + PYREC_NARGS)) { + if (field >= pycalcRecordA && field < (pycalcRecordA + PYCALCREC_NARGS)) { *no_elements = 1; - } else if (field == pyRecordVAL) { + } else if (field == pycalcRecordVAL) { *no_elements = 1; } else { - errlogPrintf("pyRecord::getArrayInfo called for %s.%s\n", rec->name, paddr->pfldDes->name); + errlogPrintf("pycalcRecord::getArrayInfo called for %s.%s\n", rec->name, paddr->pfldDes->name); } *offset = 0; diff --git a/src/pyRecord.dbd b/src/pycalcRecord.dbd similarity index 98% rename from src/pyRecord.dbd rename to src/pycalcRecord.dbd index 4004347..cb28914 100644 --- a/src/pyRecord.dbd +++ b/src/pycalcRecord.dbd @@ -1,13 +1,13 @@ -recordtype(py) { +recordtype(pycalc) { include "dbCommon.dbd" - %#define PYREC_NARGS 10 + %#define PYCALCREC_NARGS 10 field(CTX,DBF_NOACCESS) { prompt("Record Private") special(SPC_NOMOD) interest(4) - extra("struct PyRecordContext *ctx") + extra("struct PyCalcRecordContext *ctx") } field(CALC,DBF_STRING) { prompt("Python code to execute") diff --git a/testApp/Db/Makefile b/testApp/Db/Makefile index 4f27b22..5f16f94 100644 --- a/testApp/Db/Makefile +++ b/testApp/Db/Makefile @@ -11,7 +11,7 @@ include $(TOP)/configure/CONFIG # Create and install (or just install) into /db # databases, templates, substitutions like this DB += pydevtest.db -DB += pyrectest.db +DB += pycalcrectest.db #---------------------------------------------------- # If .db template is not named *.template add diff --git a/testApp/Db/pycalcrectest.db b/testApp/Db/pycalcrectest.db new file mode 100644 index 0000000..714b5eb --- /dev/null +++ b/testApp/Db/pycalcrectest.db @@ -0,0 +1,19 @@ +record(ai, "PyCalcTest:Input1") { + field(INP, "1") +} +record(ai, "PyCalcTest:Input2") { + field(INP, "6.4") +} +record(pycalc, "PyCalcTest:MathExpr") { + field(INPA, "17") + field(INPB, "3") + field(CALC, "A*B") +} +record(pycalc, "PyCalcTest:AdaptiveTypes") { + field(INPA, "PyCalcTest:Input1 CP") + field(INPB, "PyCalcTest:Input2 CP") + field(CALC, "pow(max([A, B]), 2)") +} +record(pycalc, "PyCalcTest:InvalidAlarm") { + field(CALC, "unknown_function()") +} diff --git a/testApp/Db/pyrectest.db b/testApp/Db/pyrectest.db deleted file mode 100644 index c0a74dd..0000000 --- a/testApp/Db/pyrectest.db +++ /dev/null @@ -1,19 +0,0 @@ -record(ai, "PyRecTest:Input1") { - field(INP, "1") -} -record(ai, "PyRecTest:Input2") { - field(INP, "6.4") -} -record(py, "PyRecTest:MathExpr") { - field(INPA, "17") - field(INPB, "3") - field(CALC, "%A%*%B%") -} -record(py, "PyRecTest:AdaptiveTypes") { - field(INPA, "Test:Input1 CP") - field(INPB, "Test:Input2 CP") - field(CALC, "pow(max([%A%, %B%]), 2)") -} -record(py, "PyRecTest:InvalidAlarm") { - field(CALC, "unknown_function()") -} diff --git a/testApp/src/Makefile b/testApp/src/Makefile index 59892ae..bd970d5 100644 --- a/testApp/src/Makefile +++ b/testApp/src/Makefile @@ -18,7 +18,7 @@ DBD += pydevioc.dbd # pydev.dbd will be made up from these files: pydevioc_DBD += base.dbd pydevioc_DBD += pydev.dbd -pydevioc_DBD += pyRecord.dbd +pydevioc_DBD += pycalcRecord.dbd # Include dbd files from all support applications: #pydevioc_DBD += xxx.dbd