Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions devsupApp/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ _dbapi_SRCS += pyDevSupCommon_registerRecordDeviceDriver.cpp
_dbapi_LIBS += $(EPICS_BASE_IOC_LIBS)

PY += devsup/__init__.py
PY += devsup/_dbapi.dbd
PY += devsup/_int64.dbd
PY += devsup/_lsilso.dbd
PY += devsup/db.py
PY += devsup/dset.py
PY += devsup/hooks.py
Expand Down
65 changes: 34 additions & 31 deletions devsupApp/src/dbfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#undef _POSIX_C_SOURCE
#undef _XOPEN_SOURCE

#include <Python.h>
#ifdef HAVE_NUMPY
#include <numpy/ndarrayobject.h>
#endif
Expand Down Expand Up @@ -117,9 +116,9 @@ static int assign_array(DBADDR *paddr, PyObject *arr)
{
#ifdef HAVE_NUMPY
void *rawfield = paddr->pfield;
rset *prset;
PyObject *aval;
PyArrayObject *array = (PyArrayObject *)arr;
rset *prset = NULL;
PyArrayObject *aval = NULL;
PyArrayObject * array = (PyArrayObject *)arr;
unsigned elemsize = dbValueSize(paddr->field_type);
unsigned long maxlen = paddr->no_elements, insize;
PyArray_Descr *desc = dbf2np[paddr->field_type];
Expand All @@ -139,18 +138,20 @@ static int assign_array(DBADDR *paddr, PyObject *arr)

insize = PyArray_DIM(array, 0);

if(paddr->special==SPC_DBADDR &&
(prset=dbGetRset(paddr)) &&
prset->get_array_info)
if(paddr->special==SPC_DBADDR)
{
/* array */
char *datasave=paddr->pfield;
long noe, off;
if(prset->get_array_info(paddr, &noe, &off)) {
PyErr_Format(PyExc_ValueError, "Error fetching array info for %s.%s",
paddr->precord->name,
paddr->pfldDes->name);
return 1;
prset = prset=dbGetRset(paddr);
void *datasave=paddr->pfield;
if (prset->get_array_info)
{
/* array */
long noe, off;
if(prset->get_array_info(paddr, &noe, &off)) {
PyErr_Format(PyExc_ValueError, "Error fetching array info for %s.%s",
paddr->precord->name,
paddr->pfldDes->name);
return 1;
}
}

rawfield = paddr->pfield;
Expand All @@ -159,28 +160,26 @@ static int assign_array(DBADDR *paddr, PyObject *arr)
}

Py_XINCREF(desc);
if(!(aval = PyArray_FromAny(arr, desc, 1, 2, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED | NPY_ARRAY_WRITEABLE, arr)))
if(!(aval = (PyArrayObject *)PyArray_FromAny(arr, desc, 1, 2, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED | NPY_ARRAY_WRITEABLE, arr)))
return 1;

if(elemsize!=PyArray_ITEMSIZE((PyArrayObject *)aval)) {
if(elemsize!=PyArray_ITEMSIZE(aval)) {
PyErr_Format(PyExc_AssertionError, "item size mismatch %u %u",
elemsize, (unsigned)PyArray_ITEMSIZE((PyArrayObject *)aval));
Py_DECREF(aval);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Py_DECREF(aval); is required to avoid a memory leak. That was added here: eafb208

elemsize, (unsigned)PyArray_ITEMSIZE(aval) );
return 1;
}

memcpy(rawfield, PyArray_GETPTR1((PyArrayObject *)aval, 0), insize*elemsize);
memcpy(rawfield, PyArray_GETPTR1(aval, 0), insize*elemsize);

Py_DECREF(aval);

if(paddr->special==SPC_DBADDR &&
(prset=dbGetRset(paddr)) &&
prset->get_array_info)
if(prset)
{
if(prset->put_array_info(paddr, insize)) {
PyErr_Format(PyExc_ValueError, "Error setting array info for %s.%s",
paddr->precord->name,
paddr->pfldDes->name);
if (prset->put_array_info)
if(prset->put_array_info(paddr, insize)) {
PyErr_Format(PyExc_ValueError, "Error setting array info for %s.%s",
paddr->precord->name,
paddr->pfldDes->name);
return 1;
}
}
Expand Down Expand Up @@ -304,8 +303,8 @@ static PyObject* pyField_putval(pyField *self, PyObject* args)
OP(LONG, epicsInt32, PyInt_AsLong);
OP(ULONG, epicsUInt32, PyInt_AsLong);
#ifdef HAVE_INT64
OP(INT64, epicsInt32, PyLong_AsLongLong);
OP(UINT64, epicsUInt32, PyLong_AsLongLong);
OP(INT64, epicsInt64, PyLong_AsLongLong);
OP(UINT64, epicsUInt64, PyLong_AsLongLong);
#endif
OP(FLOAT, epicsFloat32,PyFloat_AsDouble);
OP(DOUBLE,epicsFloat64,PyFloat_AsDouble);
Expand All @@ -322,11 +321,15 @@ static PyObject* pyField_putval(pyField *self, PyObject* args)
fld = PyString_AsString(val);
#endif
if(fld) {
strncpy(dest, fld, MAX_STRING_SIZE);
dest[MAX_STRING_SIZE-1]='\0';
strncpy(dest, fld, self->addr.field_size);
dest[self->addr.field_size-1]='\0';
} else {
dest[0] = '\0';
}
if (self->addr.special == SPC_MOD)
/* This is needed for long string support. */
if (prset = dbGetRset(&self->addr))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

../dbfield.c: In function ‘pyField_putval’:
../dbfield.c:332:17: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
  332 |             if (prset = dbGetRset(&self->addr))
      |                 ^~~~~

there is a compiler warning here. Could you add the extra parentheses to get rid of it?

prset->special(&self->addr, 1);
#if PY_MAJOR_VERSION >= 3
Py_DECREF(data);
#endif
Expand Down
37 changes: 11 additions & 26 deletions devsupApp/src/devsup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,32 +52,17 @@ def _init(iocMain=False):
path=os.path.join(XEPICS_BASE, "dbd"))
_dbapi._dbd_rrd_base()

with tempfile.NamedTemporaryFile() as F:
F.write("""
device(longin, INST_IO, pydevsupComIn, "Python Device")
device(longout, INST_IO, pydevsupComOut, "Python Device")

device(ai, INST_IO, pydevsupComIn, "Python Device")
device(ao, INST_IO, pydevsupComOut, "Python Device")

device(stringin, INST_IO, pydevsupComIn, "Python Device")
device(stringout, INST_IO, pydevsupComOut, "Python Device")

device(bi, INST_IO, pydevsupComIn, "Python Device")
device(bo, INST_IO, pydevsupComOut, "Python Device")

device(mbbi, INST_IO, pydevsupComIn, "Python Device")
device(mbbo, INST_IO, pydevsupComOut, "Python Device")

device(mbbiDirect, INST_IO, pydevsupComIn, "Python Device")
device(mbboDirect, INST_IO, pydevsupComOut, "Python Device")

device(waveform, INST_IO, pydevsupComIn, "Python Device")
device(aai, INST_IO, pydevsupComIn, "Python Device")
device(aao, INST_IO, pydevsupComOut, "Python Device")
""".encode('ascii'))
F.flush()
_dbapi.dbReadDatabase(F.name)
dirname = os.path.dirname(__file__)
dbd_name = dirname + "/_dbapi.dbd"
_dbapi.dbReadDatabase(dbd_name)
if epicsver >= (3, 15, 0, 2):
# Long strings are impletemented.
dbd_name = dirname + "/_lsilso.dbd"
_dbapi.dbReadDatabase(dbd_name)
if epicsver >= (3, 16, 1, 0):
# Long ints are impletemented.
dbd_name = dirname + "/_int64.dbd"
_dbapi.dbReadDatabase(dbd_name)
_dbapi._dbd_setup()

def _fini(iocMain=False):
Expand Down
22 changes: 22 additions & 0 deletions devsupApp/src/devsup/_dbapi.dbd
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

device(longin, INST_IO, pydevsupComIn, "Python Device")
device(longout, INST_IO, pydevsupComOut, "Python Device")
device(calcout, INST_IO, pydevsupComOut, "Python Device")

device(ai, INST_IO, pydevsupComIn, "Python Device")
device(ao, INST_IO, pydevsupComOut, "Python Device")

device(stringin, INST_IO, pydevsupComIn, "Python Device")
device(stringout, INST_IO, pydevsupComOut, "Python Device")
device(bi, INST_IO, pydevsupComIn, "Python Device")
device(bo, INST_IO, pydevsupComOut, "Python Device")

device(mbbi, INST_IO, pydevsupComIn, "Python Device")
device(mbbo, INST_IO, pydevsupComOut, "Python Device")

device(mbbiDirect, INST_IO, pydevsupComIn, "Python Device")
device(mbboDirect, INST_IO, pydevsupComOut, "Python Device")

device(waveform, INST_IO, pydevsupComIn, "Python Device")
device(aai, INST_IO, pydevsupComIn, "Python Device")
device(aao, INST_IO, pydevsupComOut, "Python Device")
2 changes: 2 additions & 0 deletions devsupApp/src/devsup/_int64.dbd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
device(int64in, INST_IO, pydevsupComIn, "Python Device")
device(int64out, INST_IO, pydevsupComOut, "Python Device")
2 changes: 2 additions & 0 deletions devsupApp/src/devsup/_lsilso.dbd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
device(lsi, INST_IO, pydevsupComIn, "Python Device")
device(lso, INST_IO, pydevsupComOut, "Python Device")
102 changes: 101 additions & 1 deletion devsupApp/src/devsup/test/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import unittest
import tempfile
import time

import numpy
from numpy.testing import assert_array_almost_equal, assert_array_equal
Expand Down Expand Up @@ -129,7 +130,6 @@ def test_wf_string(self):
assert_array_equal(rec.VAL,
numpy.asarray(["zero", "", "one", "This is a really long string which shoul", "", "last"], dtype='S40'))


class TestDset(IOCHelper):
db = """
record(longin, "rec:li") {
Expand Down Expand Up @@ -163,3 +163,103 @@ def test_increment(self):
with rec:
self.assertEqual(rec.VAL, 1)
self.assertEqual(rec.UDF, 0)

class TestLongStringField(IOCHelper):
db = """
record(lsi, "rec:lsi") {
field(SIZV, 128)
field(SCAN, "I/O Intr")
}
record(lso, "rec:lso") {
field(SIZV, 128)
field(DOL, "rec:lsi.VAL$")
field(OMSL, "closed_loop")
}
"""
if _dbapi.epicsver[:4] < (3, 15, 0, 2):
# Long strings not impletemented yet.
db = None

def test_lsilso(self):
if _dbapi.epicsver[:4] < (3, 15, 0, 2):
# Long strings not impletemented yet.
return

lsi = getRecord("rec:lsi")
lso = getRecord("rec:lso")

with lsi:
self.assertEqual(lsi.VAL, "")

lsi.VAL = "test"
self.assertEqual(lsi.VAL, "test")

lsi.VAL = ""
self.assertEqual(lsi.VAL, "")

# does not truncate
lsi.VAL = "This is a really long string which should NOT be truncated"
self.assertEqual(lsi.VAL, "This is a really long string which should NOT be truncated")

lso.scan()
time.sleep(0.01) # The linked value needs a small amount of time to update.

with lso:
self.assertEqual(lso.VAL, lsi.VAL)

class TestInt64Field(IOCHelper):
db = """
record(int64in, "rec:in64") {
field(SCAN, "I/O Intr")
}
record(int64out, "rec:out64") {
field(DOL, "rec:in64.VAL")
field(OMSL, "closed_loop")
}
"""
if _dbapi.epicsver[:4] < (3, 16, 1, 0):
# Long ints not implemented yet.
db = None

def testint64(self):
if _dbapi.epicsver[:4] < (3, 16, 1, 0):
# Long ints not implemented yet.
return
in64 = getRecord("rec:in64")
out64 = getRecord("rec:out64")

with in64:
self.assertEqual(in64.VAL, 0)

in64.VAL = 42
self.assertEqual(in64.VAL, 42)

in64.VAL = 0x7FFFFFFFFFFFFFFE
self.assertEqual(in64.VAL, 0x7FFFFFFFFFFFFFFE)

in64.VAL = 0x7FFFFFFFFFFFFFFF
self.assertEqual(in64.VAL, 0x7FFFFFFFFFFFFFFF)

out64.scan()
time.sleep(0.01) # The linked value needs a small amount of time to update.

with out64:
self.assertEqual(out64.VAL, in64.VAL)

class TestCalcOutRecord(IOCHelper):
db = """
record(calcout, "rec:calcout") {
field(OOPT, "On Change")
field(INPA, "0")
field(INPB, "0")
field(CALC, "A+B")
}
"""
def test_calcoutrecord(self):
rec = getRecord('rec:calcout')
self.assertEqual(rec.VAL, 0)
rec.A = 40
rec.B = 2
self.assertEqual(rec.scan(sync=True), 0)
self.assertEqual(rec.VAL, 42)

Loading