add AsyncOffload
device support helper for async. (eg. aSub)
This commit is contained in:
@ -74,6 +74,7 @@ softIocPy$(PY_VER)_LIBS += $(EPICS_BASE_IOC_LIBS)
|
|||||||
PY += devsup/__init__.py
|
PY += devsup/__init__.py
|
||||||
PY += devsup/_nullapi.py
|
PY += devsup/_nullapi.py
|
||||||
PY += devsup/db.py
|
PY += devsup/db.py
|
||||||
|
PY += devsup/dset.py
|
||||||
PY += devsup/hooks.py
|
PY += devsup/hooks.py
|
||||||
PY += devsup/interfaces.py
|
PY += devsup/interfaces.py
|
||||||
PY += devsup/util.py
|
PY += devsup/util.py
|
||||||
|
117
devsupApp/src/devsup/dset.py
Normal file
117
devsupApp/src/devsup/dset.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Some general purpose device supports
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
_log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
import time
|
||||||
|
from .hooks import initHook
|
||||||
|
|
||||||
|
class AsyncOffload(object):
|
||||||
|
"""A device support which off-loads processing to a worker thread.
|
||||||
|
|
||||||
|
>>> class ArrSum(AsyncOffload):
|
||||||
|
inputs = {'A':'one', 'B':'two'}
|
||||||
|
outputs= {'VALA':'result'}
|
||||||
|
|
||||||
|
def inThread(one=0, two=0):
|
||||||
|
return {'result':one+two}
|
||||||
|
|
||||||
|
|
||||||
|
The attribute "worker" may be set to an instance of Worker, either
|
||||||
|
statically, or in the derived class constructor (before calling the Base
|
||||||
|
class constructor).
|
||||||
|
If this attribute remains None, then a new Worker thread is created for
|
||||||
|
each record instance.
|
||||||
|
|
||||||
|
The attribute "scan" may similarly be set to an instance of IOScanListBlock.
|
||||||
|
|
||||||
|
The "inputs" and "outputs" dictionary exist to map record Field names
|
||||||
|
to internal names. This also serves to indicate which fields the inThread
|
||||||
|
methods may use. This avoid processing of unnecesary (array) fields.
|
||||||
|
"""
|
||||||
|
|
||||||
|
worker = None
|
||||||
|
scan = None
|
||||||
|
|
||||||
|
inputs = {}
|
||||||
|
outputs = {}
|
||||||
|
timefld = None
|
||||||
|
|
||||||
|
def __init__(self, rec, link):
|
||||||
|
self.rec, self.link = rec, link
|
||||||
|
|
||||||
|
if self.worker is None:
|
||||||
|
from devsup.util import Worker
|
||||||
|
self.worker = Worker(max=1)
|
||||||
|
|
||||||
|
@initHook('AtIocExit')
|
||||||
|
def _exit():
|
||||||
|
print 'stop worker for', rec.NAME
|
||||||
|
self.worker.join()
|
||||||
|
|
||||||
|
self.worker.start()
|
||||||
|
|
||||||
|
assert self.worker is not None, "Offload requires a worker thread"
|
||||||
|
|
||||||
|
I = []
|
||||||
|
for fld,name in self.inputs.items():
|
||||||
|
F = rec.field(fld)
|
||||||
|
I.append((fld, name, F.fieldinfo()[2]!=1))
|
||||||
|
|
||||||
|
O = list(self.outputs.items())
|
||||||
|
|
||||||
|
self._inputs, self._outputs = I, O
|
||||||
|
|
||||||
|
def allowScan(self, rec):
|
||||||
|
if self.scan:
|
||||||
|
return self.scan.add(rec)
|
||||||
|
|
||||||
|
def detach(self, rec):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def process(self, rec, reason=None):
|
||||||
|
if reason is None:
|
||||||
|
self._tstart = time.time()
|
||||||
|
|
||||||
|
V = {}
|
||||||
|
for fld,name,arr in self._inputs:
|
||||||
|
val = getattr(rec, fld)
|
||||||
|
if arr:
|
||||||
|
val = val.copy()
|
||||||
|
V[name] = val
|
||||||
|
|
||||||
|
self.worker.add(self._wrap, args=(rec,), kws=V)
|
||||||
|
|
||||||
|
rec.asyncStart()
|
||||||
|
|
||||||
|
else:
|
||||||
|
result = reason
|
||||||
|
if result['ok']:
|
||||||
|
for fld,name in self._outputs:
|
||||||
|
setattr(rec, fld, result.get(name, 0))
|
||||||
|
|
||||||
|
sevr = result.get('severity', 0)
|
||||||
|
if sevr:
|
||||||
|
rec.setSevr(sevr)
|
||||||
|
|
||||||
|
else:
|
||||||
|
rec.setSevr()
|
||||||
|
|
||||||
|
self._tend = time.time()
|
||||||
|
if self.timefld:
|
||||||
|
setattr(rec, self.timefld, self._tend-self._tstart)
|
||||||
|
|
||||||
|
def _wrap(self, rec, **kws):
|
||||||
|
try:
|
||||||
|
result = self.inThread(**kws)
|
||||||
|
result.setdefault('ok', True)
|
||||||
|
rec.asyncFinish(reason=result)
|
||||||
|
except:
|
||||||
|
_log.exception('Error while processing %s', rec.NAME)
|
||||||
|
rec.asyncFinish(reason={'ok':False})
|
||||||
|
|
||||||
|
def inThread(self, **kws):
|
||||||
|
return {'ok':False}
|
Reference in New Issue
Block a user