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,27 +53,26 @@ class _Record(object):
|
||||
def scan(self, sync=False, reason=None, force=0):
|
||||
"""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 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 scannined immidately on the current thread.
|
||||
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 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=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.
|
||||
"""
|
||||
@ -154,7 +153,7 @@ class _Field(object):
|
||||
def putval(self, val):
|
||||
"""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):
|
||||
@ -179,11 +178,11 @@ class _Field(object):
|
||||
"""Set the number of active elements in field's 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):
|
||||
"""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.
|
||||
"""
|
||||
|
@ -23,7 +23,7 @@ def getRecord(name):
|
||||
full record name.
|
||||
|
||||
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")
|
||||
Record("my:record:name")
|
||||
@ -38,7 +38,7 @@ def getRecord(name):
|
||||
class IOScanListBlock(object):
|
||||
"""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.
|
||||
"""
|
||||
def __init__(self):
|
||||
@ -218,7 +218,7 @@ class Record(_dbapi._Record):
|
||||
"""Lookup field in this record
|
||||
|
||||
:rtype: :class:`Field`
|
||||
:throws: KeyError for non-existant fields.
|
||||
:throws: KeyError for non-existent fields.
|
||||
|
||||
The returned object is cached so future calls will
|
||||
return the same instance.
|
||||
@ -248,6 +248,9 @@ class Record(_dbapi._Record):
|
||||
|
||||
Has not effect if the TSE field is not set to -2.
|
||||
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'):
|
||||
ts = time.mktime(ts.timetuple())
|
||||
@ -296,7 +299,7 @@ class Field(_dbapi._Field):
|
||||
"""Get timestamp of link target.
|
||||
|
||||
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
|
||||
"""
|
||||
|
@ -17,7 +17,7 @@ __all__ = [
|
||||
'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()
|
||||
|
||||
# action types
|
||||
@ -40,7 +40,7 @@ def _add_action(self, act, fn):
|
||||
class Parameter(object):
|
||||
"""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.
|
||||
|
||||
>>> class MyTable(TableBase):
|
||||
@ -100,7 +100,7 @@ class Parameter(object):
|
||||
class ParameterGroup(object):
|
||||
"""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.
|
||||
|
||||
>>> class MyTable(TableBase):
|
||||
@ -116,7 +116,7 @@ class ParameterGroup(object):
|
||||
self.params, self.name = params, name
|
||||
def onproc(self, fn):
|
||||
"""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):
|
||||
A, B = Parameter(), Parameter()
|
||||
@ -133,7 +133,7 @@ class ParameterGroup(object):
|
||||
"Decorator to run an action when any parameters has an invalid value"
|
||||
return _add_action(self, (any, lambda p:not p.isvalid), fn)
|
||||
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 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`.
|
||||
|
||||
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):
|
||||
...
|
||||
@ -282,7 +282,7 @@ class TableBase(object):
|
||||
self._parameters = {}
|
||||
|
||||
# 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 = {}
|
||||
rgroups = {}
|
||||
for k,v in self.__class__.__dict__.items():
|
||||
|
@ -16,12 +16,12 @@ devsup Package
|
||||
|
||||
.. module:: devsup.db
|
||||
|
||||
.. autofunction:: devsup.db.getRecord
|
||||
.. autofunction:: getRecord
|
||||
|
||||
:class:`Record` Class
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. class:: devsup.db.Record
|
||||
.. class:: Record
|
||||
|
||||
Allows access to a single record instance.
|
||||
*Record* instances can be created for any record in
|
||||
@ -65,7 +65,7 @@ devsup Package
|
||||
:class:`Field` Class
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autoclass:: devsup.db.Field
|
||||
.. autoclass:: Field
|
||||
|
||||
.. automethod:: name
|
||||
|
||||
@ -77,18 +77,22 @@ devsup Package
|
||||
|
||||
.. automethod:: getarray
|
||||
|
||||
.. automethod:: getarraylen
|
||||
|
||||
.. automethod:: putarraylen
|
||||
|
||||
.. automethod:: fieldinfo
|
||||
|
||||
.. automethod:: getTime
|
||||
|
||||
.. automethod:: getAlarm
|
||||
|
||||
.. autoclass:: devsup.db.IOScanListBlock
|
||||
.. autoclass:: IOScanListBlock
|
||||
:members:
|
||||
:inherited-members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: devsup.db.IOScanListThread
|
||||
.. autoclass:: IOScanListThread
|
||||
:members: add, interrupt
|
||||
|
||||
|
||||
|
@ -62,8 +62,8 @@ The following should be added to individual EPICS Makefiles. ::
|
||||
include $(TOP)/configure/RULES
|
||||
include $(PYDEVSUP)/configure/RULES_PY
|
||||
|
||||
This will add or ammend several make variables. The ``USR_*FLAGS`` variables
|
||||
may be extended with approprate flags for building python modules. The ``PY_VER``
|
||||
This will add or amend several make variables. The ``USR_*FLAGS`` variables
|
||||
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").
|
||||
The ``PY_LD_VER`` variable is defined with the python library version number (eg "3.2mu"),
|
||||
which may be the same as ``PY_VER``.
|
||||
@ -94,7 +94,7 @@ Additional .py files can be installed as follows. ::
|
||||
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
|
||||
the resulting library is expected to provide the an initialization function
|
||||
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::
|
||||
:maxdepth: 4
|
||||
|
||||
gettingstarted
|
||||
environment
|
||||
devsup
|
||||
interfaces
|
||||
|
@ -55,7 +55,7 @@ and the string "some other string".
|
||||
of the methods which all Python device support instances must provide.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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):
|
||||
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):
|
||||
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