44import itertools
55import logging
66
7- from .datatypes import DatatypeEnum , struct_dict , is_numeric , is_float
7+ from .datatypes import DatatypeEnum , struct_dict , is_numeric , is_float , range_dict
88from .callback_handler import CallbackHandler , FailMode
99
1010
@@ -27,15 +27,36 @@ class Variable:
2727 def __post_init__ (self ):
2828 if self .datatype not in DatatypeEnum :
2929 raise ValueError ("Unsupported datatype" )
30+
3031 if self .access not in ("rw" , "ro" , "wo" , "const" ):
3132 raise ValueError ("Invalid access type" )
33+
3234 if not is_numeric (self .datatype ) and (
3335 self .maximum is not None or self .minimum is not None
3436 ):
3537 raise ValueError (
3638 f"Minimum and Maximum not available with datatype { self .datatype !r} "
3739 )
3840
41+ if range_dict .get (self .datatype , None ) is not None :
42+ minimum , maximum = range_dict [self .datatype ]
43+
44+ if self .minimum is not None and self .minimum < minimum :
45+ raise ValueError (
46+ f"Specified minimum of { self .minimum } is lower than datatype supported minimum of { minimum } "
47+ )
48+
49+ if self .maximum is not None and self .maximum > maximum :
50+ raise ValueError (
51+ f"Specified maximum of { self .maximum } is higher than datatype supported maximum of { maximum } "
52+ )
53+
54+ if self .minimum is None :
55+ self .minimum = minimum
56+
57+ if self .maximum is None :
58+ self .maximum = maximum
59+
3960 @property
4061 def writable (self ):
4162 return self .access in ("wo" , "rw" )
@@ -178,15 +199,27 @@ def __setitem__(self, index: int, obj: TObject):
178199 self ._objects [index ] = obj
179200
180201 def lookup (self , index : int , subindex : int = None ) -> TObject :
202+ """Return object on index:subindex in object dictionary. When subindex is None
203+ the object is returned. Otherwise a lookup is extended with subindex in the Array/Record.
204+
205+ :param index: index in object dictionary
206+ :param subindex: optional subindex to lookup inside object
207+ :returns: the Variable, Record or Array
208+
209+ :raises KeyError: when object not found in dictionary
210+ """
181211 try :
182- return self ._variables [index ]
183- except KeyError :
212+ if index in self ._variables :
213+ return self ._variables [index ]
214+
184215 if subindex is None :
185216 return self ._objects [index ]
186217
187218 obj = self ._objects [index ]
188219 assert isinstance (obj , (Record , Array )), "Record or Array expected"
189220 return obj [subindex ]
221+ except KeyError :
222+ raise KeyError ("Object {index}:{subindex} not in object dictionary" )
190223
191224 def write (self , index : int , subindex : int , value : Any , downloaded : bool = False ):
192225 """Write the given value to the according variable.
@@ -197,6 +230,9 @@ def write(self, index: int, subindex: int, value: Any, downloaded: bool = False)
197230 :param value: value to be written
198231 :param downloaded: flag is set, when the write is caused by an actual download
199232 (instead of a internal value change)
233+
234+ :raises KeyError: when index:subindex not found
235+ :raises Exception: when validate_callback fails
200236 """
201237 assert isinstance (
202238 value , (bytes , bool , int , float )
@@ -207,6 +243,19 @@ def write(self, index: int, subindex: int, value: Any, downloaded: bool = False)
207243 else :
208244 multiplexor = (index , subindex )
209245
246+ variable = self .lookup (* multiplexor ) # check if variable exists
247+
248+ if not downloaded :
249+ if variable .minimum is not None and value < variable .minimum :
250+ raise ValueError (
251+ f"Value { value } is too low (minimum is { variable .minimum } )"
252+ )
253+
254+ if variable .maximum is not None and value > variable .maximum :
255+ raise ValueError (
256+ f"Value { value } is too high (minimum is { variable .maximum } )"
257+ )
258+
210259 if multiplexor in self .validate_callbacks :
211260 self .validate_callbacks [multiplexor ].call (value ) # may raises exception
212261
0 commit comments