TupleOf and StructOf create multiple curves
the components of TupleOf and StructOf are storead as separate curves in history Change-Id: I42ebf84653408de7148796763a4d4ea9dd309696
This commit is contained in:
parent
e0f9b4a858
commit
2d310bc612
@ -21,15 +21,40 @@
|
|||||||
import time
|
import time
|
||||||
from secop.datatypes import get_datatype, IntRange, FloatRange, ScaledInteger,\
|
from secop.datatypes import get_datatype, IntRange, FloatRange, ScaledInteger,\
|
||||||
EnumType, BoolType, StringType, TupleOf, StructOf
|
EnumType, BoolType, StringType, TupleOf, StructOf
|
||||||
import history.histwriter
|
import histreader.histwriter as histwriter
|
||||||
|
|
||||||
|
|
||||||
class HistWriter(history.histwriter.HistWriter):
|
def make_cvt_list(dt, tail=''):
|
||||||
|
"""create conversion list
|
||||||
|
|
||||||
|
list of tuple (<conversion function>, <tail>, <curve options>)
|
||||||
|
tail is a postfix to be appended in case of tuples and structs
|
||||||
|
"""
|
||||||
|
if isinstance(dt, (EnumType, IntRange, BoolType)):
|
||||||
|
return[(int, tail, dict(type='NUM'))]
|
||||||
|
if isinstance(dt, (FloatRange, ScaledInteger)):
|
||||||
|
return [(dt.import_value, tail, dict(type='NUM', unit=dt.unit, period=5) if dt.unit else {})]
|
||||||
|
if isinstance(dt, StringType):
|
||||||
|
return [(lambda x: x, tail, dict(type='STR'))]
|
||||||
|
if isinstance(dt, TupleOf):
|
||||||
|
items = enumerate(dt.members)
|
||||||
|
elif isinstance(dt, StructOf):
|
||||||
|
items = dt.members.items()
|
||||||
|
else:
|
||||||
|
return [] # ArrayType, BlobType and TextType are ignored: too much data, probably not used
|
||||||
|
result = []
|
||||||
|
for subkey, elmtype in items:
|
||||||
|
for fun, tail, opts in make_cvt_list(elmtype, '%s.%s' % (tail, subkey)):
|
||||||
|
result.append((lambda v, k=subkey, f=fun: f(v[k]), tail, opts))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class HistWriter(histwriter.HistWriter):
|
||||||
"""extend writer to be used as an internal frappy connection"""
|
"""extend writer to be used as an internal frappy connection"""
|
||||||
def __init__(self, directory, predefined_names, dispatcher):
|
def __init__(self, directory, predefined_names, dispatcher):
|
||||||
super().__init__(directory)
|
super().__init__(directory)
|
||||||
self.predefined_names = predefined_names
|
self.predefined_names = predefined_names
|
||||||
self.parameters = {} # dict <mod:param> of (<datatype>, <hist key>)
|
self.cvt_lists = {} # dict <mod:param> of <conversion list>
|
||||||
self.activated = False
|
self.activated = False
|
||||||
self.dispatcher = dispatcher
|
self.dispatcher = dispatcher
|
||||||
self._init_time = None
|
self._init_time = None
|
||||||
@ -38,7 +63,6 @@ class HistWriter(history.histwriter.HistWriter):
|
|||||||
def init(self, msg):
|
def init(self, msg):
|
||||||
action, _, description = msg
|
action, _, description = msg
|
||||||
assert action == 'describing'
|
assert action == 'describing'
|
||||||
vars = []
|
|
||||||
self._init_time = time.time()
|
self._init_time = time.time()
|
||||||
|
|
||||||
for modname, moddesc in description['modules'].items():
|
for modname, moddesc in description['modules'].items():
|
||||||
@ -47,14 +71,19 @@ class HistWriter(history.histwriter.HistWriter):
|
|||||||
if pname.startswith('_') and pname[1:] not in self.predefined_names:
|
if pname.startswith('_') and pname[1:] not in self.predefined_names:
|
||||||
key = modname + ':' + pname[1:]
|
key = modname + ':' + pname[1:]
|
||||||
dt = get_datatype(pdesc['datainfo'])
|
dt = get_datatype(pdesc['datainfo'])
|
||||||
|
cvt_list = make_cvt_list(dt, key)
|
||||||
|
for _, hkey, opts in cvt_list:
|
||||||
if pname == 'value':
|
if pname == 'value':
|
||||||
continuous = isinstance(dt, (FloatRange, ScaledInteger))
|
opts['period'] = opts.get('period', 0)
|
||||||
vars.append('%s|%s|%s||%d' % (key, dt.unit or '1', modname, continuous))
|
opts['show'] = True
|
||||||
|
opts['label'] = modname
|
||||||
elif pname == 'target':
|
elif pname == 'target':
|
||||||
vars.append('%s|%s|%s_target||0' % (key, dt.unit or '1', modname))
|
opts['period'] = 0
|
||||||
self.parameters[ident] = dt, key
|
opts['label'] = modname + '_target'
|
||||||
self.put(self._init_time, 'STR', 'vars', ' '.join(vars))
|
opts['show'] = True
|
||||||
|
self.put_def(hkey, opts)
|
||||||
|
self.cvt_lists[ident] = cvt_list
|
||||||
|
# self.put(self._init_time, 'STR', 'vars', ' '.join(vars))
|
||||||
self.dispatcher.handle_activate(self, None, None)
|
self.dispatcher.handle_activate(self, None, None)
|
||||||
self._init_time = None
|
self._init_time = None
|
||||||
return
|
return
|
||||||
@ -64,45 +93,17 @@ class HistWriter(history.histwriter.HistWriter):
|
|||||||
if not action.endswith('update'):
|
if not action.endswith('update'):
|
||||||
print('unknown async message %r' % msg)
|
print('unknown async message %r' % msg)
|
||||||
return
|
return
|
||||||
now = self._init_time or time.time() # on initialisation, make all timestamps equal
|
now = self._init_time or time.time() # on initialisation, us the same timestamp for all
|
||||||
dt, key = self.parameters[ident]
|
|
||||||
if action == 'update':
|
if action == 'update':
|
||||||
|
for fun, key, opts in self.cvt_lists[ident]:
|
||||||
def convert(value, dt, key):
|
# we only look at the value, qualifiers are ignored for now
|
||||||
if isinstance(dt, (EnumType, IntRange, BoolType)):
|
# we do not use the timestamp here, as a potentially decreasing value might
|
||||||
return [('NUM', key, str(int(value)))]
|
# bring the reader software into trouble
|
||||||
if isinstance(dt, (FloatRange, ScaledInteger)):
|
self.put(now, key, str(fun(value[0])))
|
||||||
return [('NUM', key, str(dt.import_value(value)))]
|
|
||||||
if isinstance(dt, StringType):
|
|
||||||
return [('STR', key, value)]
|
|
||||||
if isinstance(dt, TupleOf):
|
|
||||||
return sum((convert(value[i], d, '%s.%s' % (key, i)) for i, d in enumerate(dt.members)), [])
|
|
||||||
if isinstance(dt, StructOf):
|
|
||||||
return sum((convert(value[k], d, '%s.%s' % (key, k)) for d, k in dt.members.items()), [])
|
|
||||||
# ArrayType, BlobType and TextType are not considered: too much data, proabably not used
|
|
||||||
return []
|
|
||||||
|
|
||||||
# omit qualifiers. we do not use the timestamp here, as a potentially decreasing
|
|
||||||
# values might get the reader software into trouble
|
|
||||||
result = convert(value[0], dt, key)
|
|
||||||
for htype, key, strval in convert(value[0], dt, key):
|
|
||||||
self.put(now, htype, key, strval)
|
|
||||||
|
|
||||||
else: # error_update
|
else: # error_update
|
||||||
|
for _, key, _ in self.cvt_lists[ident]:
|
||||||
old = self.cache.get(key)
|
old = self.cache.get(key)
|
||||||
if old is None:
|
if old is None:
|
||||||
return # ignore if this key is not yet used
|
return # ignore if this key is not yet used
|
||||||
|
self.put(now, key, '')
|
||||||
def get_keys(dt, key):
|
|
||||||
if isinstance(dt, (IntRange, FloatRange, ScaledInteger, BoolType, EnumType)):
|
|
||||||
return [('NUM', key)]
|
|
||||||
if isinstance(dt, StringType):
|
|
||||||
return [('STR', key)]
|
|
||||||
if isinstance(dt, TupleOf):
|
|
||||||
return sum((get_keys(d, '%s.%s' % (key, i)) for i, d in enumerate(dt.members)), [])
|
|
||||||
if isinstance(dt, StructOf):
|
|
||||||
return sum((get_keys(d, '%s.%s' % (key, k)) for d, k in dt.members.items()), [])
|
|
||||||
return []
|
|
||||||
|
|
||||||
for htype, key in get_keys(dt, key):
|
|
||||||
self.put(now, htype, key, '')
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user