update documentation

This commit is contained in:
Michael Davidsaver
2014-06-16 17:55:15 -04:00
parent 9b5b9687b4
commit ee2cb409c2
11 changed files with 121 additions and 33 deletions

8
README.md Normal file
View 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)

View File

@ -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.
""" """

View File

@ -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
""" """

View File

@ -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():

View File

@ -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

View File

@ -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". ::

View 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.

View File

@ -15,6 +15,7 @@ Contents:
.. toctree:: .. toctree::
:maxdepth: 4 :maxdepth: 4
gettingstarted
environment environment
devsup devsup
interfaces interfaces

View File

@ -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
View 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
View File

@ -0,0 +1,5 @@
record(longin, "test:count") {
field(DTYP, "Python Device")
field(INP , "@cntmod")
field(SCAN, "1 second")
}