validator should take resolution into account

also changed names: according to the meeting in 2019-01-16 we
decided to use absolute_resolution/relative_resolution
instead of _precision

Change-Id: I4a49bb745901b87c2aa2bc2728fd7a44026421e0
Reviewed-on: https://forge.frm2.tum.de/review/20321
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
zolliker 2019-04-03 13:33:07 +02:00 committed by Enrico Faulhaber
parent bc33f263ec
commit 0eb68e54be
2 changed files with 36 additions and 38 deletions

View File

@ -108,24 +108,24 @@ class FloatRange(DataType):
"""Restricted float type"""
def __init__(self, minval=None, maxval=None, unit=None, fmtstr=None,
absolute_precision=None, relative_precision=None,):
absolute_resolution=None, relative_resolution=None,):
self._defaults = {}
self.setprop('min', minval, float(u'-inf'), float)
self.setprop('max', maxval, float(u'+inf'), float)
self.setprop('unit', unit, u'', unicode)
self.setprop('fmtstr', fmtstr, u'%g', unicode)
self.setprop('absolute_precision', absolute_precision, 0.0, float)
self.setprop('relative_precision', relative_precision, 1.2e-7, float)
self.setprop('absolute_resolution', absolute_resolution, 0.0, float)
self.setprop('relative_resolution', relative_resolution, 1.2e-7, float)
# check values
if self.min > self.max:
raise ValueError(u'Max must be larger then min!')
raise ValueError(u'max must be larger then min!')
if '%' not in self.fmtstr:
raise ValueError(u'Invalid fmtstr!')
if self.absolute_precision < 0:
raise ValueError(u'absolute_precision MUST be >=0')
if self.relative_precision < 0:
raise ValueError(u'relative_precision MUST be >=0')
if self.absolute_resolution < 0:
raise ValueError(u'absolute_resolution MUST be >=0')
if self.relative_resolution < 0:
raise ValueError(u'relative_resolution MUST be >=0')
@property
def as_json(self):
@ -137,17 +137,10 @@ class FloatRange(DataType):
value = float(value)
except Exception:
raise ValueError(u'Can not validate %r to float' % value)
if self.min is not None and value < self.min:
raise ValueError(u'%r should not be less then %s' %
(value, self.min))
if self.max is not None and value > self.max:
raise ValueError(u'%r should not be greater than %s' %
(value, self.max))
if None in (self.min, self.max):
return value
if self.min <= value <= self.max:
return value
raise ValueError(u'%r should be an float between %.3f and %.3f' %
prec = max(abs(value * self.relative_resolution), self.absolute_resolution)
if self.min - prec <= value <= self.max + prec:
return min(max(value, self.min), self.max)
raise ValueError(u'%g should be a float between %g and %g' %
(value, self.min, self.max))
def __repr__(self):
@ -228,15 +221,15 @@ class ScaledInteger(DataType):
the scale is only used for calculating to/from transport serialisation"""
def __init__(self, scale, minval=None, maxval=None, unit=None, fmtstr=None,
absolute_precision=None, relative_precision=None,):
absolute_resolution=None, relative_resolution=None,):
self._defaults = {}
self.scale = float(scale)
if not self.scale > 0:
raise ValueError(u'Scale MUST be positive!')
self.setprop('unit', unit, u'', unicode)
self.setprop('fmtstr', fmtstr, u'%g', unicode)
self.setprop('absolute_precision', absolute_precision, self.scale, float)
self.setprop('relative_precision', relative_precision, 1.2e-7, float)
self.setprop('absolute_resolution', absolute_resolution, self.scale, float)
self.setprop('relative_resolution', relative_resolution, 1.2e-7, float)
self.min = DEFAULT_MIN_INT * self.scale if minval is None else float(minval)
self.max = DEFAULT_MAX_INT * self.scale if maxval is None else float(maxval)
@ -246,10 +239,10 @@ class ScaledInteger(DataType):
raise ValueError(u'Max must be larger then min!')
if '%' not in self.fmtstr:
raise ValueError(u'Invalid fmtstr!')
if self.absolute_precision < 0:
raise ValueError(u'absolute_precision MUST be >=0')
if self.relative_precision < 0:
raise ValueError(u'relative_precision MUST be >=0')
if self.absolute_resolution < 0:
raise ValueError(u'absolute_resolution MUST be >=0')
if self.relative_resolution < 0:
raise ValueError(u'relative_resolution MUST be >=0')
@property
def as_json(self):
@ -265,11 +258,12 @@ class ScaledInteger(DataType):
value = float(value)
except Exception:
raise ValueError(u'Can not validate %r to float' % value)
if value < self.min:
raise ValueError(u'%r should be a float between %d and %d' %
(value, self.min, self.max))
if value > self.max:
raise ValueError(u'%r should be a float between %d and %d' %
prec = max(self.scale, abs(value * self.relative_resolution),
self.absolute_resolution)
if self.min - prec <= value <= self.max + prec:
value = min(max(value, self.min), self.max)
else:
raise ValueError(u'%g should be a float between %g and %g' %
(value, self.min, self.max))
intval = int((value + self.scale * 0.5) // self.scale)
value = float(intval * self.scale)

View File

@ -56,6 +56,7 @@ def test_FloatRange():
dt.validate([19, u'X'])
dt.validate(1)
dt.validate(0)
dt.validate(13.14 - 10) # raises an error, if resolution is not handled correctly
assert dt.export_value(-2.718) == -2.718
assert dt.import_value(-2.718) == -2.718
with pytest.raises(ValueError):
@ -64,11 +65,11 @@ def test_FloatRange():
dt = FloatRange()
assert dt.as_json == [u'double', {}]
dt = FloatRange(unit=u'X', fmtstr=u'%r', absolute_precision=1,
relative_precision=0.1)
dt = FloatRange(unit=u'X', fmtstr=u'%r', absolute_resolution=1,
relative_resolution=0.1)
assert dt.as_json == [u'double', {u'unit':u'X', u'fmtstr':u'%r',
u'absolute_precision':1,
u'relative_precision':0.1}]
u'absolute_resolution':1,
u'relative_resolution':0.1}]
assert dt.validate(4) == 4
assert dt.format_value(3.14) == u'3.14 X'
assert dt.format_value(3.14, u'') == u'3.14'
@ -126,15 +127,18 @@ def test_ScaledInteger():
assert dt.import_value(272) == 2.72
dt = ScaledInteger(0.003, 0, 1, unit=u'X', fmtstr=u'%r',
absolute_precision=1, relative_precision=0.1)
absolute_resolution=0.001, relative_resolution=1e-5)
assert dt.as_json == [u'scaled', {u'scale':0.003,u'min':0,u'max':333,
u'unit':u'X', u'fmtstr':u'%r',
u'absolute_precision':1,
u'relative_precision':0.1}]
u'absolute_resolution':0.001,
u'relative_resolution':1e-5}]
assert dt.validate(0.4) == 0.399
assert dt.format_value(0.4) == u'0.4 X'
assert dt.format_value(0.4, u'') == u'0.4'
assert dt.format_value(0.4, u'Z') == u'0.4 Z'
assert dt.validate(1.0029) == 0.999
with pytest.raises(ValueError):
dt.validate(1.004)
def test_EnumType():