apply main unit also in structured types

Change-Id: I5a3efb167f2b460b847d8e7ac75a21848976b5f8
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/29350
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
zolliker 2022-09-14 14:50:39 +02:00
parent 71aaf7187a
commit eaefa1ce87
3 changed files with 50 additions and 8 deletions

View File

@ -124,6 +124,9 @@ class DataType(HasProperties):
"""
raise NotImplementedError
def set_main_unit(self, unit):
"""replace $ in unit by argument"""
class Stub(DataType):
"""incomplete datatype, to be replaced with a proper one later during module load
@ -155,9 +158,17 @@ class Stub(DataType):
prop.datatype = globals()[stub.name](*stub.args, **stub.kwds)
class HasUnit:
unit = Property('physical unit', Stub('StringType', isUTF8=True), extname='unit', default='')
def set_main_unit(self, unit):
if '$' in self.unit:
self.setProperty('unit', self.unit.replace('$', unit))
# SECoP types:
class FloatRange(DataType):
class FloatRange(HasUnit, DataType):
"""(restricted) float type
:param minval: (property **min**)
@ -166,7 +177,6 @@ class FloatRange(DataType):
"""
min = Property('low limit', Stub('FloatRange'), extname='min', default=-sys.float_info.max)
max = Property('high limit', Stub('FloatRange'), extname='max', default=sys.float_info.max)
unit = Property('physical unit', Stub('StringType', isUTF8=True), extname='unit', default='')
fmtstr = Property('format string', Stub('StringType'), extname='fmtstr', default='%g')
absolute_resolution = Property('absolute resolution', Stub('FloatRange', 0),
extname='absolute_resolution', default=0.0)
@ -331,7 +341,7 @@ class IntRange(DataType):
raise BadValueError('incompatible datatypes')
class ScaledInteger(DataType):
class ScaledInteger(HasUnit, DataType):
"""scaled integer (= fixed resolution float) type
:param minval: (property **min**)
@ -344,7 +354,6 @@ class ScaledInteger(DataType):
scale = Property('scale factor', FloatRange(sys.float_info.min), extname='scale', mandatory=True)
min = Property('low limit', FloatRange(), extname='min', mandatory=True)
max = Property('high limit', FloatRange(), extname='max', mandatory=True)
unit = Property('physical unit', Stub('StringType', isUTF8=True), extname='unit', default='')
fmtstr = Property('format string', Stub('StringType'), extname='fmtstr', default='%g')
absolute_resolution = Property('absolute resolution', FloatRange(0),
extname='absolute_resolution', default=0.0)
@ -806,6 +815,9 @@ class ArrayOf(DataType):
except AttributeError:
raise BadValueError('incompatible datatypes') from None
def set_main_unit(self, unit):
self.members.set_main_unit(unit)
class TupleOf(DataType):
"""data structure with fields of inhomogeneous type
@ -872,6 +884,10 @@ class TupleOf(DataType):
for a, b in zip(self.members, other.members):
a.compatible(b)
def set_main_unit(self, unit):
for member in self.members:
member.set_main_unit(unit)
class ImmutableDict(dict):
def _no(self, *args, **kwds):
@ -961,6 +977,10 @@ class StructOf(DataType):
except (AttributeError, TypeError, KeyError):
raise BadValueError('incompatible datatypes') from None
def set_main_unit(self, unit):
for member in self.members.values():
member.set_main_unit(unit)
class CommandType(DataType):
"""command

View File

@ -457,10 +457,12 @@ class Module(HasAccessibles):
aobj.finish()
# Modify units AFTER applying the cfgdict
for pname, pobj in self.parameters.items():
dt = pobj.datatype
if '$' in dt.unit:
dt.setProperty('unit', dt.unit.replace('$', self.parameters['value'].datatype.unit))
mainvalue = self.parameters.get('value')
if mainvalue:
mainunit = mainvalue.datatype.unit
if mainunit:
for pname, pobj in self.parameters.items():
pobj.datatype.set_main_unit(mainunit)
# 6) check complete configuration of * properties
if not errors:

View File

@ -680,3 +680,23 @@ def test_lazy_validation(dt):
generalConfig.defaults['lazy_number_validation'] = False
with pytest.raises(DiscouragedConversion):
dt('0')
mytuple = TupleOf(ScaledInteger(0.1, 0, 10, unit='$'), FloatRange(unit='$/min'))
myarray = ArrayOf(mytuple)
@pytest.mark.parametrize('unit, dt', [
('m', FloatRange(unit='$/sec')),
('A', mytuple),
('V', myarray),
('X', StructOf(a=myarray, b=mytuple)),
])
def test_main_unit(unit, dt):
fixed_dt = dt.copy()
fixed_dt.set_main_unit(unit)
before = repr(dt.export_datatype())
after = repr(fixed_dt.export_datatype())
assert '$' in before
assert before != after
assert before.replace('$', unit) == after