update doc
This commit is contained in:
@ -31,7 +31,6 @@ _dbapi_SRCS += pyDevSupCommon_registerRecordDeviceDriver.cpp
|
|||||||
_dbapi_LIBS += $(EPICS_BASE_IOC_LIBS)
|
_dbapi_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||||
|
|
||||||
PY += devsup/__init__.py
|
PY += devsup/__init__.py
|
||||||
PY += devsup/_nullapi.py
|
|
||||||
PY += devsup/db.py
|
PY += devsup/db.py
|
||||||
PY += devsup/dset.py
|
PY += devsup/dset.py
|
||||||
PY += devsup/hooks.py
|
PY += devsup/hooks.py
|
||||||
|
@ -445,25 +445,28 @@ static PyObject *pyField_len(pyField *self)
|
|||||||
|
|
||||||
static PyMethodDef pyField_methods[] = {
|
static PyMethodDef pyField_methods[] = {
|
||||||
{"name", (PyCFunction)pyField_name, METH_NOARGS,
|
{"name", (PyCFunction)pyField_name, METH_NOARGS,
|
||||||
"Return Names (\"record\",\"field\")"},
|
"name() -> (recname, fldname)\n"},
|
||||||
{"fieldinfo", (PyCFunction)pyField_fldinfo, METH_NOARGS,
|
{"fieldinfo", (PyCFunction)pyField_fldinfo, METH_NOARGS,
|
||||||
"Field type info\nReturn (type, size, #elements"},
|
"fieldinfo() -> (dbf, elem_size, elem_count"},
|
||||||
{"getval", (PyCFunction)pyField_getval, METH_NOARGS,
|
{"getval", (PyCFunction)pyField_getval, METH_NOARGS,
|
||||||
"Returns scalar version of field value"},
|
"getval() -> object\n"},
|
||||||
{"putval", (PyCFunction)pyField_putval, METH_VARARGS,
|
{"putval", (PyCFunction)pyField_putval, METH_VARARGS,
|
||||||
"Sets field value from a scalar"},
|
"putval(object)\n"},
|
||||||
{"getarray", (PyCFunction)pyField_getarray, METH_NOARGS,
|
{"getarray", (PyCFunction)pyField_getarray, METH_NOARGS,
|
||||||
|
"getarray() -> numpy.ndarray\n"
|
||||||
"Return a numpy ndarray refering to this field for in-place operations."},
|
"Return a numpy ndarray refering to this field for in-place operations."},
|
||||||
{"getarraylen", (PyCFunction)pyField_getlen, METH_NOARGS,
|
{"getarraylen", (PyCFunction)pyField_getlen, METH_NOARGS,
|
||||||
|
"getarraylen() -> int\n"
|
||||||
"Return current number of valid elements for array fields."},
|
"Return current number of valid elements for array fields."},
|
||||||
{"putarraylen", (PyCFunction)pyField_setlen, METH_VARARGS,
|
{"putarraylen", (PyCFunction)pyField_setlen, METH_VARARGS,
|
||||||
|
"putarraylen(int)\n"
|
||||||
"Set number of valid elements for array fields."},
|
"Set number of valid elements for array fields."},
|
||||||
{"getTime", (PyCFunction)pyField_getTime, METH_NOARGS,
|
{"getTime", (PyCFunction)pyField_getTime, METH_NOARGS,
|
||||||
"Return link target timestamp as a tuple (sec, nsec)."},
|
"getTime() -> (sec, nsec)."},
|
||||||
{"getAlarm", (PyCFunction)pyField_getAlarm, METH_NOARGS,
|
{"getAlarm", (PyCFunction)pyField_getAlarm, METH_NOARGS,
|
||||||
"Return link target alarm condtions as a tuple (severity, status)."},
|
"getAlarm() -> (severity, status)."},
|
||||||
{"__len__", (PyCFunction)pyField_len, METH_NOARGS,
|
{"__len__", (PyCFunction)pyField_len, METH_NOARGS,
|
||||||
"Maximum number of elements storable in this field"},
|
"Maximum number of elements storable in this field."},
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -293,23 +293,32 @@ static PyObject *pyRecord_exit(pyRecord *self, PyObject *args)
|
|||||||
|
|
||||||
static PyMethodDef pyRecord_methods[] = {
|
static PyMethodDef pyRecord_methods[] = {
|
||||||
{"name", (PyCFunction)pyRecord_name, METH_NOARGS,
|
{"name", (PyCFunction)pyRecord_name, METH_NOARGS,
|
||||||
"Return record name string"},
|
"name() -> str\n\n"
|
||||||
|
"Record name. ::\n"
|
||||||
|
"\n"
|
||||||
|
" R = getRecord(\"my:record:name\")\n"
|
||||||
|
" assert R.name()==\"my:record:name\"\n"},
|
||||||
{"rtype", (PyCFunction)pyRecord_rtype, METH_NOARGS,
|
{"rtype", (PyCFunction)pyRecord_rtype, METH_NOARGS,
|
||||||
|
"rtype() -> str\n"
|
||||||
"Return record type name string"},
|
"Return record type name string"},
|
||||||
{"isPyRecord", (PyCFunction)pyRecord_ispyrec, METH_NOARGS,
|
{"isPyRecord", (PyCFunction)pyRecord_ispyrec, METH_NOARGS,
|
||||||
|
"isPyRecord() -> bool\n"
|
||||||
"Is this record using Python Device."},
|
"Is this record using Python Device."},
|
||||||
{"info", (PyCFunction)pyRecord_info, METH_VARARGS,
|
{"info", (PyCFunction)pyRecord_info, METH_VARARGS,
|
||||||
"Lookup info name\ninfo(name, def=None)"},
|
"info(key [,default]) -> str\n"
|
||||||
|
"Lookup info by name\n"
|
||||||
|
":rtype: str\n"
|
||||||
|
":throws: KeyError\n"},
|
||||||
{"infos", (PyCFunction)pyRecord_infos, METH_NOARGS,
|
{"infos", (PyCFunction)pyRecord_infos, METH_NOARGS,
|
||||||
|
"infos() -> {'name':'value'}\n"
|
||||||
"Return a dictionary of all infos for this record."},
|
"Return a dictionary of all infos for this record."},
|
||||||
{"setSevr", (PyCFunction)pyRecord_setSevr, METH_VARARGS|METH_KEYWORDS,
|
{"setSevr", (PyCFunction)pyRecord_setSevr, METH_VARARGS|METH_KEYWORDS,
|
||||||
|
"setSevr(sevr=INVALID_ALARM, stat=COMM_ALARM)\n"
|
||||||
"Set alarm new alarm severity/status. Record must be locked!"},
|
"Set alarm new alarm severity/status. Record must be locked!"},
|
||||||
{"setTime", (PyCFunction)pyRecord_setTime, METH_VARARGS,
|
{"setTime", (PyCFunction)pyRecord_setTime, METH_VARARGS,
|
||||||
"Set record timestamp if TSE==-2. Record must be locked!"},
|
"Set record timestamp if TSE==-2. Record must be locked!"},
|
||||||
{"scan", (PyCFunction)pyRecord_scan, METH_VARARGS|METH_KEYWORDS,
|
{"scan", (PyCFunction)pyRecord_scan, METH_VARARGS|METH_KEYWORDS,
|
||||||
"scan(sync=False)\nScan this record. If sync is False then"
|
"scan(sync=False, reason=None, force=0)\n"},
|
||||||
"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,
|
{"asyncStart", (PyCFunction)pyRecord_asyncStart, METH_NOARGS,
|
||||||
"Begin an asynchronous action. Record must be locked!"},
|
"Begin an asynchronous action. Record must be locked!"},
|
||||||
{"asyncFinish", (PyCFunction)pyRecord_asyncFinish, METH_VARARGS|METH_KEYWORDS,
|
{"asyncFinish", (PyCFunction)pyRecord_asyncFinish, METH_VARARGS|METH_KEYWORDS,
|
||||||
|
@ -1,205 +0,0 @@
|
|||||||
|
|
||||||
class _Record(object):
|
|
||||||
"""Handle for record operations
|
|
||||||
|
|
||||||
r = _Record("rec:name")
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, rec):
|
|
||||||
pass
|
|
||||||
def name(self):
|
|
||||||
"""Record name string.
|
|
||||||
|
|
||||||
>>> R = getRecord("my:record:name")
|
|
||||||
>>> R.name()
|
|
||||||
"my:record:name"
|
|
||||||
"""
|
|
||||||
def rtype(self):
|
|
||||||
"""Record type name string.
|
|
||||||
|
|
||||||
>>> R = getRecord("my:record:name")
|
|
||||||
>>> R.type()
|
|
||||||
"longin"
|
|
||||||
"""
|
|
||||||
def isPyRecord(self):
|
|
||||||
"""Is this record using Python device support.
|
|
||||||
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
def info(self, key):
|
|
||||||
"""info(key [,default])
|
|
||||||
|
|
||||||
:rtype: str
|
|
||||||
:throws: KeyError
|
|
||||||
|
|
||||||
Lookup record info tag. If no default
|
|
||||||
is provided then an exception is raised
|
|
||||||
if the info key does not exist.
|
|
||||||
"""
|
|
||||||
def infos(self):
|
|
||||||
"""Return a dictionary of all info tags
|
|
||||||
for this record
|
|
||||||
"""
|
|
||||||
|
|
||||||
def setSevr(self, sevr=3, stat=15):
|
|
||||||
"""setSevr(sevr=INVALID_ALARM, stat=COMM_ALARM)
|
|
||||||
|
|
||||||
Signal a new alarm condition. The effect of this
|
|
||||||
call depends on the current alarm condition.
|
|
||||||
|
|
||||||
See :c:func:`recGblSetSevr` in EPICS Base.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def scan(self, sync=False, reason=None, force=0):
|
|
||||||
"""Scan this record.
|
|
||||||
|
|
||||||
:param sync: scan in current thread (``True``), or queue to a worker (``False``).
|
|
||||||
:param reason: Reason object passed to :meth:`process <DeviceSupport.process>` (sync=True only)
|
|
||||||
:param force: Record processing condtion (0=Passive, 1=Force, 2=I/O Intr)
|
|
||||||
:throws: ``RuntimeError`` when ``sync=True``, but ``force`` prevents scanning.
|
|
||||||
|
|
||||||
If ``sync`` is False then a scan request is queued to run in another thread..
|
|
||||||
If ``sync`` is True then the record is scanned immediately on the current thread.
|
|
||||||
|
|
||||||
For ``reason`` argument must be used in conjunction with ``sync=True``
|
|
||||||
on records with Python device support. This provides a means
|
|
||||||
of providing extra contextual information to the record's
|
|
||||||
:meth:`process <DeviceSupport.process>` method.
|
|
||||||
|
|
||||||
``force`` is used to decide if the record will actually be processed,
|
|
||||||
``force=0`` will only process records with SCAN=Passive.
|
|
||||||
``force=1`` will process any record if at all possible.
|
|
||||||
``force=2`` will only process records with Python device support and
|
|
||||||
SCAN=I/O Intr.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
It is **never** safe to use ``sync=True`` while holding record locks,
|
|
||||||
including from within a *process* method.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def asyncStart(self):
|
|
||||||
"""Start asynchronous processing
|
|
||||||
|
|
||||||
This method may be called from a device support
|
|
||||||
:meth:`process <DeviceSupport.process>` method
|
|
||||||
to indicate that processing will continue
|
|
||||||
later.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
This method is **only** safe to call within a *process* method.
|
|
||||||
"""
|
|
||||||
def asyncFinish(self, reason=None):
|
|
||||||
"""Indicate that asynchronous processing can complete
|
|
||||||
|
|
||||||
Similar to :meth:`scan`. Used to conclude asynchronous
|
|
||||||
process started with :meth:`asyncStart`.
|
|
||||||
|
|
||||||
Processing is completed on the current thread.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
This method should **never** be called within
|
|
||||||
a :meth:`process <DeviceSupport.process>` method,
|
|
||||||
or any other context where a Record lock is held.
|
|
||||||
Doing so will result in a deadlock.
|
|
||||||
|
|
||||||
Typically a *reason* will be passed to *process* as a way
|
|
||||||
of indicating that this is the completion of an async action. ::
|
|
||||||
|
|
||||||
AsyncDone = object()
|
|
||||||
class MySup(object):
|
|
||||||
def process(record, reason):
|
|
||||||
if reason is AsyncDone:
|
|
||||||
record.VAL = ... # store result
|
|
||||||
else:
|
|
||||||
threading.Timer(1.0, record.asyncFinish, kwargs={'reason':AsyncDone})
|
|
||||||
record.asyncStart()
|
|
||||||
"""
|
|
||||||
|
|
||||||
class _Field(object):
|
|
||||||
"""Handle for field operations
|
|
||||||
|
|
||||||
f = Field("rec:name.HOPR")
|
|
||||||
|
|
||||||
Field objects implement the buffer protocol.
|
|
||||||
"""
|
|
||||||
def __init__(self, fld):
|
|
||||||
pass
|
|
||||||
def name(self):
|
|
||||||
"""Fetch the record and field names.
|
|
||||||
|
|
||||||
>>> FLD = getRecord("rec").field("FLD")
|
|
||||||
>>> FLD.name()
|
|
||||||
("rec", "FLD")
|
|
||||||
"""
|
|
||||||
def fieldinfo(self):
|
|
||||||
"""(type, size, #elements) = fieldinfo()
|
|
||||||
|
|
||||||
Type is DBF type code
|
|
||||||
size is number of bytes to start a single element
|
|
||||||
#elements is the maximum number of elements the field can hold
|
|
||||||
"""
|
|
||||||
|
|
||||||
def getval(self):
|
|
||||||
"""Fetch the current field value as a scalar or numpy.ndarray.
|
|
||||||
|
|
||||||
:rtype: int, float, str, or ndarray
|
|
||||||
|
|
||||||
Returned type depends of field DBF type.
|
|
||||||
An ``int`` is returned for CHAR, SHORT, LONG, and ENUM.
|
|
||||||
A ``float`` is returned for FLOAT and DOUBLE.
|
|
||||||
A ``str`` is returned for STRING.
|
|
||||||
A ``numpy.ndarray`` is returned for array fields.
|
|
||||||
This array is read-only and has the size of the present valid values.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
It is only safe to read this ndarray while the record
|
|
||||||
lock is held (ie within :meth:`process <DeviceSupport.process>`).
|
|
||||||
"""
|
|
||||||
|
|
||||||
def putval(self, val):
|
|
||||||
"""Update the field value
|
|
||||||
|
|
||||||
Must be an Int, Float, str, or numpy.ndarray.
|
|
||||||
Strings will be truncated to 39 characters.
|
|
||||||
Arrays must have a size less than or equal to the max element count.
|
|
||||||
Arrays are converted as necessary to the field's native type.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def getarray(self):
|
|
||||||
"""Return a numpy ndarray refering to this field for in-place operations.
|
|
||||||
|
|
||||||
The dtype of the ndarray will correspond to the field's DBF type.
|
|
||||||
Its size will be the **maximum** number of elements.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
It is only safe to read or write to this ndarray while the record
|
|
||||||
lock is held (ie within :meth:`process <DeviceSupport.process>`).
|
|
||||||
"""
|
|
||||||
|
|
||||||
def getarraylen(self):
|
|
||||||
"""Return the number of active elements for the field.
|
|
||||||
|
|
||||||
>>> F = Field(...)
|
|
||||||
>>> assert len(F)>=F.getarraylen()
|
|
||||||
"""
|
|
||||||
|
|
||||||
def putarraylen(self, len):
|
|
||||||
"""Set the number of active elements in field's array.
|
|
||||||
|
|
||||||
Requires that the underlying field be an array.
|
|
||||||
Must be greater than one and less than or equal to the maximum length of the field.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def getAlarm(self):
|
|
||||||
"""Returns a tuple (severity, status) with the condition of the linked field.
|
|
||||||
|
|
||||||
Only works for fields of type DBF_INLINK.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
"""Returns the maximum number of elements which may be stored in the field.
|
|
||||||
|
|
||||||
This is always 1 for scalar fields.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_hooks = {}
|
|
@ -260,6 +260,76 @@ class Record(_dbapi._Record):
|
|||||||
|
|
||||||
super(Record, self).setTime(sec, nsec)
|
super(Record, self).setTime(sec, nsec)
|
||||||
|
|
||||||
|
def scan(self, *args, **kws):
|
||||||
|
"""scan(sync=False, reason=None, force=0)
|
||||||
|
Scan this record.
|
||||||
|
|
||||||
|
:param sync: scan in current thread (``True``), or queue to a worker (``False``).
|
||||||
|
:param reason: Reason object passed to :meth:`process <DeviceSupport.process>` (sync=True only)
|
||||||
|
:param force: Record processing condtion (0=Passive, 1=Force, 2=I/O Intr)
|
||||||
|
:throws: ``RuntimeError`` when ``sync=True``, but ``force`` prevents scanning.
|
||||||
|
|
||||||
|
If ``sync`` is False then a scan request is queued to run in another thread..
|
||||||
|
If ``sync`` is True then the record is scanned immediately on the current thread.
|
||||||
|
|
||||||
|
For ``reason`` argument must be used in conjunction with ``sync=True``
|
||||||
|
on records with Python device support. This provides a means
|
||||||
|
of providing extra contextual information to the record's
|
||||||
|
:meth:`process <DeviceSupport.process>` method.
|
||||||
|
|
||||||
|
``force`` is used to decide if the record will actually be processed,
|
||||||
|
``force=0`` will only process records with SCAN=Passive.
|
||||||
|
``force=1`` will process any record if at all possible.
|
||||||
|
``force=2`` will only process records with Python device support and
|
||||||
|
SCAN=I/O Intr.
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
It is **never** safe to use ``sync=True`` while holding record locks,
|
||||||
|
including from within a *process* method.
|
||||||
|
"""
|
||||||
|
return _dbapi._Record.scan(self, *args, **kws)
|
||||||
|
|
||||||
|
def asyncStart(self):
|
||||||
|
"""Start asynchronous processing
|
||||||
|
|
||||||
|
This method may be called from a device support
|
||||||
|
:meth:`process <DeviceSupport.process>` method
|
||||||
|
to indicate that processing will continue
|
||||||
|
later.
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
This method is **only** safe to call within a *process* method.
|
||||||
|
"""
|
||||||
|
return _dbapi._Record.asyncStart(self)
|
||||||
|
|
||||||
|
def asyncFinish(self, reason=None):
|
||||||
|
"""Indicate that asynchronous processing can complete
|
||||||
|
|
||||||
|
Similar to :meth:`scan`. Used to conclude asynchronous
|
||||||
|
process started with :meth:`asyncStart`.
|
||||||
|
|
||||||
|
Processing is completed on the current thread.
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
This method should **never** be called within
|
||||||
|
a :meth:`process <DeviceSupport.process>` method,
|
||||||
|
or any other context where a Record lock is held.
|
||||||
|
Doing so will result in a deadlock.
|
||||||
|
|
||||||
|
Typically a *reason* will be passed to *process* as a way
|
||||||
|
of indicating that this is the completion of an async action. ::
|
||||||
|
|
||||||
|
AsyncDone = object()
|
||||||
|
class MySup(object):
|
||||||
|
def process(record, reason):
|
||||||
|
if reason is AsyncDone:
|
||||||
|
record.VAL = ... # store result
|
||||||
|
else:
|
||||||
|
threading.Timer(1.0, record.asyncFinish, kwargs={'reason':AsyncDone})
|
||||||
|
record.asyncStart()
|
||||||
|
"""
|
||||||
|
return _dbapi._Record.asyncStart(self, reason=reason)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
try:
|
try:
|
||||||
F = self.field(name)
|
F = self.field(name)
|
||||||
|
@ -42,7 +42,7 @@ def initHook(state):
|
|||||||
|
|
||||||
@initHook("AfterIocRunning")
|
@initHook("AfterIocRunning")
|
||||||
def myfn():
|
def myfn():
|
||||||
# do stuff
|
pass
|
||||||
"""
|
"""
|
||||||
def _add(fn):
|
def _add(fn):
|
||||||
addHook(state, fn)
|
addHook(state, fn)
|
||||||
|
@ -108,7 +108,7 @@ In somefile.c ::
|
|||||||
PyMODINIT_FUNC init_myextname(void)
|
PyMODINIT_FUNC init_myextname(void)
|
||||||
|
|
||||||
Installing for several Python versions
|
Installing for several Python versions
|
||||||
------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
The recipe for building and installing the pyDevSup module
|
The recipe for building and installing the pyDevSup module
|
||||||
for several python version side by side is ::
|
for several python version side by side is ::
|
||||||
|
Reference in New Issue
Block a user