update documentation
This commit is contained in:
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
pyDevSup
|
||||||
|
========
|
||||||
|
|
||||||
|
EPICS Device support in Python.
|
||||||
|
|
||||||
|
See [documentation](http://mdavidsaver.github.io/pyDevSup)
|
||||||
|
|
||||||
|
For file [releases](http://sourceforge.net/projects/epics/files/pyDevSup)
|
@ -53,29 +53,28 @@ class _Record(object):
|
|||||||
def scan(self, sync=False, reason=None, force=0):
|
def scan(self, sync=False, reason=None, force=0):
|
||||||
"""Scan this record.
|
"""Scan this record.
|
||||||
|
|
||||||
:param sync: scan in current thread (``True``), or queue (``False``).
|
: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 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)
|
:param force: Record processing condtion (0=Passive, 1=Force, 2=I/O Intr)
|
||||||
:throws: ``RuntimeError`` when ``sync=True``, but ``force`` prevents scanning.
|
:throws: ``RuntimeError`` when ``sync=True``, but ``force`` prevents scanning.
|
||||||
|
|
||||||
If ``sync`` is False then a
|
If ``sync`` is False then a scan request is queued to run in another thread..
|
||||||
scan request is queued to run in another thread..
|
If ``sync`` is True then the record is scanned immediately on the current thread.
|
||||||
If ``sync`` is True then the record
|
|
||||||
is scannined immidately on the current thread.
|
|
||||||
|
|
||||||
For ``reason`` argument must be used in conjunction with ``sync=True``
|
For ``reason`` argument must be used in conjunction with ``sync=True``
|
||||||
on records with Python device support. This provides a means
|
on records with Python device support. This provides a means
|
||||||
of providing extra contextual information to the record's
|
of providing extra contextual information to the record's
|
||||||
:meth:`process <DeviceSupport.process>` method.
|
:meth:`process <DeviceSupport.process>` method.
|
||||||
|
|
||||||
``force`` is used to decide if the record will actuall be processed,
|
``force`` is used to decide if the record will actually be processed,
|
||||||
``force=0`` will only process records with SCAN=Passive.
|
``force=0`` will only process records with SCAN=Passive.
|
||||||
``force=1`` will process any record if at all possible.
|
``force=1`` will process any record if at all possible.
|
||||||
``force=2`` will only process records with Python device support and
|
``force=2`` will only process records with Python device support and
|
||||||
SCAN=I/O Intr.
|
SCAN=I/O Intr.
|
||||||
|
|
||||||
It is **never** safe to use ``sync=True`` while holding record locks,
|
.. important::
|
||||||
including from within a *process* method.
|
It is **never** safe to use ``sync=True`` while holding record locks,
|
||||||
|
including from within a *process* method.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def asyncStart(self):
|
def asyncStart(self):
|
||||||
@ -154,7 +153,7 @@ class _Field(object):
|
|||||||
def putval(self, val):
|
def putval(self, val):
|
||||||
"""Update the field value
|
"""Update the field value
|
||||||
|
|
||||||
Must be an Int, Float or str. Strings will be truncated to 39 charactors.
|
Must be an Int, Float or str. Strings will be truncated to 39 characters.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getarray(self):
|
def getarray(self):
|
||||||
@ -179,11 +178,11 @@ class _Field(object):
|
|||||||
"""Set the number of active elements in field's array.
|
"""Set the number of active elements in field's array.
|
||||||
|
|
||||||
Requires that the underlying field be an array.
|
Requires that the underlying field be an array.
|
||||||
Must be less than the maximum length of the field.
|
Must be greater than one and less than or equal to the maximum length of the field.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getAlarm(self):
|
def getAlarm(self):
|
||||||
"""Returns a tuple (severity, status) with the condtion of the linked field.
|
"""Returns a tuple (severity, status) with the condition of the linked field.
|
||||||
|
|
||||||
Only works for fields of type DBF_INLINK.
|
Only works for fields of type DBF_INLINK.
|
||||||
"""
|
"""
|
||||||
|
@ -23,7 +23,7 @@ def getRecord(name):
|
|||||||
full record name.
|
full record name.
|
||||||
|
|
||||||
The result is cached so the future calls will return the same instance.
|
The result is cached so the future calls will return the same instance.
|
||||||
This is the prefered way to get :class:`Record` instances.
|
This is the preferred way to get :class:`Record` instances.
|
||||||
|
|
||||||
>>> R = getRecord("my:record:name")
|
>>> R = getRecord("my:record:name")
|
||||||
Record("my:record:name")
|
Record("my:record:name")
|
||||||
@ -38,7 +38,7 @@ def getRecord(name):
|
|||||||
class IOScanListBlock(object):
|
class IOScanListBlock(object):
|
||||||
"""A list of records which will be processed together.
|
"""A list of records which will be processed together.
|
||||||
|
|
||||||
This convienence class to handle the accounting to
|
This convenience class to handle the accounting to
|
||||||
maintain a list of records.
|
maintain a list of records.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -218,7 +218,7 @@ class Record(_dbapi._Record):
|
|||||||
"""Lookup field in this record
|
"""Lookup field in this record
|
||||||
|
|
||||||
:rtype: :class:`Field`
|
:rtype: :class:`Field`
|
||||||
:throws: KeyError for non-existant fields.
|
:throws: KeyError for non-existent fields.
|
||||||
|
|
||||||
The returned object is cached so future calls will
|
The returned object is cached so future calls will
|
||||||
return the same instance.
|
return the same instance.
|
||||||
@ -248,6 +248,9 @@ class Record(_dbapi._Record):
|
|||||||
|
|
||||||
Has not effect if the TSE field is not set to -2.
|
Has not effect if the TSE field is not set to -2.
|
||||||
All inputs must be referenced to the posix epoch.
|
All inputs must be referenced to the posix epoch.
|
||||||
|
|
||||||
|
If a datetime is provided, it must use the local system
|
||||||
|
timezone.
|
||||||
"""
|
"""
|
||||||
if hasattr(ts, 'timetuple'):
|
if hasattr(ts, 'timetuple'):
|
||||||
ts = time.mktime(ts.timetuple())
|
ts = time.mktime(ts.timetuple())
|
||||||
@ -296,7 +299,7 @@ class Field(_dbapi._Field):
|
|||||||
"""Get timestamp of link target.
|
"""Get timestamp of link target.
|
||||||
|
|
||||||
Only works for DBF_INLINK fields.
|
Only works for DBF_INLINK fields.
|
||||||
Returns the time in seconds since the posix epoch.
|
Returns the time in seconds since the POSIX epoch.
|
||||||
|
|
||||||
:rtype: float
|
:rtype: float
|
||||||
"""
|
"""
|
||||||
|
@ -17,7 +17,7 @@ __all__ = [
|
|||||||
'build'
|
'build'
|
||||||
]
|
]
|
||||||
|
|
||||||
# Reason code to cause a record to read a new value from a table paramter
|
# Reason code to cause a record to read a new value from a table parameter
|
||||||
_INTERNAL = object()
|
_INTERNAL = object()
|
||||||
|
|
||||||
# action types
|
# action types
|
||||||
@ -40,7 +40,7 @@ def _add_action(self, act, fn):
|
|||||||
class Parameter(object):
|
class Parameter(object):
|
||||||
"""Define a parameter in a table.
|
"""Define a parameter in a table.
|
||||||
|
|
||||||
When a sub-class of TableBase is instancianted, parameters become
|
When a sub-class of TableBase is instantiated, parameters become
|
||||||
py:class:`_ParamInstance` instances.
|
py:class:`_ParamInstance` instances.
|
||||||
|
|
||||||
>>> class MyTable(TableBase):
|
>>> class MyTable(TableBase):
|
||||||
@ -100,7 +100,7 @@ class Parameter(object):
|
|||||||
class ParameterGroup(object):
|
class ParameterGroup(object):
|
||||||
"""A helper for defining actions on groups of parameters
|
"""A helper for defining actions on groups of parameters
|
||||||
|
|
||||||
When a sub-class of TableBase is instancianted, parameter groups become
|
When a sub-class of TableBase is instantiated, parameter groups become
|
||||||
py:class:`_ParamGroupInstance` instances.
|
py:class:`_ParamGroupInstance` instances.
|
||||||
|
|
||||||
>>> class MyTable(TableBase):
|
>>> class MyTable(TableBase):
|
||||||
@ -116,7 +116,7 @@ class ParameterGroup(object):
|
|||||||
self.params, self.name = params, name
|
self.params, self.name = params, name
|
||||||
def onproc(self, fn):
|
def onproc(self, fn):
|
||||||
"""Decorator run a member function action whenever
|
"""Decorator run a member function action whenever
|
||||||
a device support attached to any paramter in the group processes.
|
a device support attached to any parameter in the group processes.
|
||||||
|
|
||||||
>>> class MyTable(TableBase):
|
>>> class MyTable(TableBase):
|
||||||
A, B = Parameter(), Parameter()
|
A, B = Parameter(), Parameter()
|
||||||
@ -133,7 +133,7 @@ class ParameterGroup(object):
|
|||||||
"Decorator to run an action when any parameters has an invalid value"
|
"Decorator to run an action when any parameters has an invalid value"
|
||||||
return _add_action(self, (any, lambda p:not p.isvalid), fn)
|
return _add_action(self, (any, lambda p:not p.isvalid), fn)
|
||||||
def oncondition(self, fmap, freduce=all):
|
def oncondition(self, fmap, freduce=all):
|
||||||
"""Decorator for a custom condtion.
|
"""Decorator for a custom condition.
|
||||||
|
|
||||||
The condition is specified in two parts, a map function, and a reduce function.
|
The condition is specified in two parts, a map function, and a reduce function.
|
||||||
The map function is applied to each parameter in the group. Then a list
|
The map function is applied to each parameter in the group. Then a list
|
||||||
@ -265,7 +265,7 @@ class TableBase(object):
|
|||||||
|
|
||||||
Sub-class this and populate with :py:class:`Parameter` and :py:class:`ParameterGroup`.
|
Sub-class this and populate with :py:class:`Parameter` and :py:class:`ParameterGroup`.
|
||||||
|
|
||||||
When a table is instanciated it must be given a unique name.
|
#When a table is instantiated it must be given a unique name.
|
||||||
|
|
||||||
>>> class MyTable(TableBase):
|
>>> class MyTable(TableBase):
|
||||||
...
|
...
|
||||||
@ -282,7 +282,7 @@ class TableBase(object):
|
|||||||
self._parameters = {}
|
self._parameters = {}
|
||||||
|
|
||||||
# Find Parameters and ParameterGroup in the class dictionary
|
# Find Parameters and ParameterGroup in the class dictionary
|
||||||
# and place approprate things in the instance dictionary
|
# and place appropriate things in the instance dictionary
|
||||||
rparams = {}
|
rparams = {}
|
||||||
rgroups = {}
|
rgroups = {}
|
||||||
for k,v in self.__class__.__dict__.items():
|
for k,v in self.__class__.__dict__.items():
|
||||||
|
@ -16,12 +16,12 @@ devsup Package
|
|||||||
|
|
||||||
.. module:: devsup.db
|
.. module:: devsup.db
|
||||||
|
|
||||||
.. autofunction:: devsup.db.getRecord
|
.. autofunction:: getRecord
|
||||||
|
|
||||||
:class:`Record` Class
|
:class:`Record` Class
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. class:: devsup.db.Record
|
.. class:: Record
|
||||||
|
|
||||||
Allows access to a single record instance.
|
Allows access to a single record instance.
|
||||||
*Record* instances can be created for any record in
|
*Record* instances can be created for any record in
|
||||||
@ -65,7 +65,7 @@ devsup Package
|
|||||||
:class:`Field` Class
|
:class:`Field` Class
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. autoclass:: devsup.db.Field
|
.. autoclass:: Field
|
||||||
|
|
||||||
.. automethod:: name
|
.. automethod:: name
|
||||||
|
|
||||||
@ -77,18 +77,22 @@ devsup Package
|
|||||||
|
|
||||||
.. automethod:: getarray
|
.. automethod:: getarray
|
||||||
|
|
||||||
|
.. automethod:: getarraylen
|
||||||
|
|
||||||
|
.. automethod:: putarraylen
|
||||||
|
|
||||||
.. automethod:: fieldinfo
|
.. automethod:: fieldinfo
|
||||||
|
|
||||||
.. automethod:: getTime
|
.. automethod:: getTime
|
||||||
|
|
||||||
.. automethod:: getAlarm
|
.. automethod:: getAlarm
|
||||||
|
|
||||||
.. autoclass:: devsup.db.IOScanListBlock
|
.. autoclass:: IOScanListBlock
|
||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
|
||||||
.. autoclass:: devsup.db.IOScanListThread
|
.. autoclass:: IOScanListThread
|
||||||
:members: add, interrupt
|
:members: add, interrupt
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ The following should be added to individual EPICS Makefiles. ::
|
|||||||
include $(TOP)/configure/RULES
|
include $(TOP)/configure/RULES
|
||||||
include $(PYDEVSUP)/configure/RULES_PY
|
include $(PYDEVSUP)/configure/RULES_PY
|
||||||
|
|
||||||
This will add or ammend several make variables. The ``USR_*FLAGS`` variables
|
This will add or amend several make variables. The ``USR_*FLAGS`` variables
|
||||||
may be extended with approprate flags for building python modules. The ``PY_VER``
|
may be extended with appropriate flags for building python modules. The ``PY_VER``
|
||||||
variable is defined with the Python version number found in install directories (eg "2.7").
|
variable is defined with the Python version number found in install directories (eg "2.7").
|
||||||
The ``PY_LD_VER`` variable is defined with the python library version number (eg "3.2mu"),
|
The ``PY_LD_VER`` variable is defined with the python library version number (eg "3.2mu"),
|
||||||
which may be the same as ``PY_VER``.
|
which may be the same as ``PY_VER``.
|
||||||
@ -94,7 +94,7 @@ Additional .py files can be installed as follows. ::
|
|||||||
Building extensions
|
Building extensions
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
For convienance, additional Python extensions can be build by the EPICS
|
For convenience, additional Python extensions can be build by the EPICS
|
||||||
build system. In this example the extension name is "_myextname" and
|
build system. In this example the extension name is "_myextname" and
|
||||||
the resulting library is expected to provide the an initialization function
|
the resulting library is expected to provide the an initialization function
|
||||||
named "init_myextname". ::
|
named "init_myextname". ::
|
||||||
|
60
documentation/gettingstarted.rst
Normal file
60
documentation/gettingstarted.rst
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
Getting Started
|
||||||
|
===============
|
||||||
|
|
||||||
|
Counter
|
||||||
|
-------
|
||||||
|
|
||||||
|
Consider a simple EPICS database with one record. Call it :download:`cntrec.db <../testApp/cntrec.db>`
|
||||||
|
|
||||||
|
.. literalinclude:: ../testApp/cntrec.db
|
||||||
|
|
||||||
|
This is creating a single record which will use the "Python Device" support code (aka this package).
|
||||||
|
It will attempt to scan (call the process method) one a second.
|
||||||
|
The *INP* field is parsed and the first work identifies the Python module which will provide
|
||||||
|
the logic behind this record (everything after the first word is passed to the module :py:func:`build` function.
|
||||||
|
|
||||||
|
Now create :download:`cntrec.db <../testApp/cntmod.py>` with the following.
|
||||||
|
|
||||||
|
.. literalinclude:: ../testApp/cntmod.py
|
||||||
|
|
||||||
|
This module is expected to provide a special callable :py:func:`build`.
|
||||||
|
We also provide a constructor and method :py:meth:`detach <DeviceSupport.detach>`
|
||||||
|
which don't do anything.
|
||||||
|
The :py:meth:`process <DeviceSupport.process>` method increments the *VAL* field of the attached :py:class:`Record <devsup.db.Record>`.
|
||||||
|
|
||||||
|
Start this IOC with. ::
|
||||||
|
|
||||||
|
$ ./bin/linux-x86_64/softIocPy2.7 -d cntrec.db
|
||||||
|
Starting iocInit
|
||||||
|
...
|
||||||
|
iocRun: All initialization complete
|
||||||
|
epics>dbl
|
||||||
|
test:count
|
||||||
|
epics>
|
||||||
|
|
||||||
|
Now in another terminal run.::
|
||||||
|
|
||||||
|
$ camonitor test:count
|
||||||
|
...
|
||||||
|
test:count 2014-06-16 16:48:22.891825 9
|
||||||
|
test:count 2014-06-16 16:48:23.891967 10
|
||||||
|
test:count 2014-06-16 16:48:24.892137 11
|
||||||
|
test:count 2014-06-16 16:48:25.892286 12
|
||||||
|
|
||||||
|
It may be necessary to run *export EPICS_CA_ADDR_LIST=localhost* first.
|
||||||
|
|
||||||
|
Additional examples and applications
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
This module comes with several examples in *testApp* as well as three complete applications.
|
||||||
|
|
||||||
|
logApp
|
||||||
|
Observes a line based text file as new lines are appended.
|
||||||
|
Writes each line to a charactor array PV.
|
||||||
|
Special handling of caPutLog files.
|
||||||
|
|
||||||
|
pidMonApp
|
||||||
|
Monitors the PID file created by a UNIX daemon.
|
||||||
|
|
||||||
|
weatherApp
|
||||||
|
Retreives weather reports via the *pymetar* module.
|
@ -15,6 +15,7 @@ Contents:
|
|||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 4
|
:maxdepth: 4
|
||||||
|
|
||||||
|
gettingstarted
|
||||||
environment
|
environment
|
||||||
devsup
|
devsup
|
||||||
interfaces
|
interfaces
|
||||||
|
@ -55,7 +55,7 @@ and the string "some other string".
|
|||||||
of the methods which all Python device support instances must provide.
|
of the methods which all Python device support instances must provide.
|
||||||
These methods will be called during the course of IOC processing.
|
These methods will be called during the course of IOC processing.
|
||||||
|
|
||||||
Execptions raised by these methods are printed to the IOC console,
|
Exceptions raised by these methods are printed to the IOC console,
|
||||||
but will otherwise be ignored.
|
but will otherwise be ignored.
|
||||||
|
|
||||||
The module :mod:`devsup.interfaces` provides a Zope Interface
|
The module :mod:`devsup.interfaces` provides a Zope Interface
|
||||||
@ -76,7 +76,7 @@ and the string "some other string".
|
|||||||
:param reason: ``None`` or an object provided when processing was requested.
|
:param reason: ``None`` or an object provided when processing was requested.
|
||||||
|
|
||||||
This method is called whenever the associated record needs to be updated
|
This method is called whenever the associated record needs to be updated
|
||||||
in responce to a request. The source of this request is typically determined
|
in response to a request. The source of this request is typically determined
|
||||||
by the record's SCAN field.
|
by the record's SCAN field.
|
||||||
|
|
||||||
The actions taken by this method will depend heavily on the application.
|
The actions taken by this method will depend heavily on the application.
|
||||||
@ -120,7 +120,7 @@ and the string "some other string".
|
|||||||
def allowScan(self, record):
|
def allowScan(self, record):
|
||||||
return self.a_scan.add(record)
|
return self.a_scan.add(record)
|
||||||
|
|
||||||
Which is most cases can be abbriviated to ::
|
Which in most cases can be abbriviated to ::
|
||||||
|
|
||||||
class MySup(object):
|
class MySup(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
8
testApp/cntmod.py
Normal file
8
testApp/cntmod.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
class MySupport:
|
||||||
|
def __init__(self, rec, link):
|
||||||
|
pass
|
||||||
|
def detach(self, rec):
|
||||||
|
pass
|
||||||
|
def process(self, rec, reason):
|
||||||
|
rec.VAL = rec.VAL + 1
|
||||||
|
build = MySupport
|
5
testApp/cntrec.db
Normal file
5
testApp/cntrec.db
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
record(longin, "test:count") {
|
||||||
|
field(DTYP, "Python Device")
|
||||||
|
field(INP , "@cntmod")
|
||||||
|
field(SCAN, "1 second")
|
||||||
|
}
|
Reference in New Issue
Block a user