@@ -93,85 +93,123 @@ class ControllerNoIO(Controller):
9393 await c .my_attr .update ()
9494
9595
96+ class DummyConnection :
97+ def __init__ (self ):
98+ self ._connected = False
99+ self ._int_value = 5
100+ self ._ro_int_value = 10
101+ self ._float_value = 7.5
102+
103+ async def connect (self ):
104+ self ._connected = True
105+
106+ async def get (self , uri : str ):
107+ if not self ._connected :
108+ raise TimeoutError ("No response from DummyConnection" )
109+ if uri == "config/introspect_api" :
110+ return [
111+ {
112+ "name" : "int_parameter" ,
113+ "subsystem" : "status" ,
114+ "dtype" : "int" ,
115+ "min" : 0 ,
116+ "max" : 100 ,
117+ "value" : self ._int_value ,
118+ "read_only" : False ,
119+ },
120+ {
121+ "name" : "ro_int_parameter" ,
122+ "subsystem" : "status" ,
123+ "dtype" : "int" ,
124+ "value" : self ._ro_int_value ,
125+ "read_only" : True ,
126+ },
127+ {
128+ "name" : "float_parameter" ,
129+ "subsystem" : "status" ,
130+ "dtype" : "float" ,
131+ "max" : 1000.0 ,
132+ "value" : self ._float_value ,
133+ "read_only" : False ,
134+ },
135+ ]
136+
137+ # increment after getting
138+ elif uri == "status/int_parameter" :
139+ value = self ._int_value
140+ self ._int_value += 1
141+ elif uri == "status/ro_int_parameter" :
142+ value = self ._ro_int_value
143+ self ._ro_int_value += 1
144+ elif uri == "status/float_parameter" :
145+ value = self ._float_value
146+ self ._float_value += 1
147+ return value
148+
149+ async def set (self , uri : str , value : float | int ):
150+ if uri == "status/int_parameter" :
151+ self ._int_value = value
152+ elif uri == "status/ro_int_parameter" :
153+ # don't update read only parameter
154+ pass
155+ elif uri == "status/float_parameter" :
156+ self ._float_value = value
157+
158+
96159@pytest .mark .asyncio ()
97160async def test_dynamic_attribute_io_specification ():
98- example_introspection_response = [
99- {
100- "name" : "int_parameter" ,
101- "dtype" : "int" ,
102- "min" : 0 ,
103- "max" : 100 ,
104- "value" : 5 ,
105- "read_only" : False ,
106- },
107- {"name" : "ro_int_parameter" , "dtype" : "int" , "value" : 10 , "read_only" : True },
108- {
109- "name" : "float_parameter" ,
110- "dtype" : "float" ,
111- "max" : 1000.0 ,
112- "value" : 7.5 ,
113- "read_only" : False ,
114- },
115- ]
116-
117161 @dataclass
118162 class DemoParameterAttributeIORef (AttributeIORef , Generic [NumberT ]):
119163 name : str
120- # TODO, this is weird, we should just use the attributes's min and max fields
121- min : NumberT | None = None
122- max : NumberT | None = None
123- read_only : bool = False
164+ subsystem : str
165+ connection : DummyConnection
166+
167+ @property
168+ def uri (self ):
169+ return f"{ self .subsystem } /{ self .name } "
124170
125171 class DemoParameterAttributeIO (AttributeIO [NumberT , DemoParameterAttributeIORef ]):
126172 async def update (
127173 self ,
128174 attr : AttrR [NumberT , DemoParameterAttributeIORef ],
129175 ):
130- # OK, so this doesn't really work when we have min and maxes...
131- await attr .set (attr . get () + 1 )
176+ value = await attr . io_ref . connection . get ( attr . io_ref . uri )
177+ await attr .set (value )
132178
133179 async def send (
134180 self ,
135181 attr : AttrW [NumberT , DemoParameterAttributeIORef ],
136182 value : NumberT ,
137183 ) -> None :
138- if (
139- attr .io_ref .read_only
140- ): # TODO, this isn't necessary as we can not call process on this anyway
141- raise RuntimeError (
142- f"Could not set read only attribute { attr .io_ref .name } "
143- )
144-
145- if (io_min := attr .io_ref .min ) is not None and value < io_min :
146- raise RuntimeError (
147- f"Could not set { attr .io_ref .name } to { value } , min is { io_min } "
148- )
149-
150- if (io_max := attr .io_ref .max ) is not None and value > io_max :
151- raise RuntimeError (
152- f"Could not set { attr .io_ref .name } to { value } , max is { io_max } "
153- )
154- # TODO: we should always end send with a update_display_without_process...
184+ await attr .io_ref .connection .set (attr .io_ref .uri , value )
185+ await self .update (attr )
155186
156187 class DemoParameterController (Controller ):
157188 ro_int_parameter : AttrR
158189 int_parameter : AttrRW
159190 float_parameter : AttrRW # hint to satisfy pyright
160191
161192 async def initialise (self ):
162- dtype_mapping = {"int" : Int (), "float" : Float ()}
193+ self ._connection = DummyConnection ()
194+ await self ._connection .connect ()
195+ dtype_mapping = {"int" : Int , "float" : Float }
196+ example_introspection_response = await self ._connection .get (
197+ "config/introspect_api"
198+ )
163199 for parameter_response in example_introspection_response :
164200 try :
165201 ro = parameter_response ["read_only" ]
166202 ref = DemoParameterAttributeIORef (
167203 name = parameter_response ["name" ],
168- min = parameter_response .get ("min" , None ),
169- max = parameter_response .get ("max" , None ),
170- read_only = ro ,
204+ subsystem = parameter_response ["subsystem" ],
205+ connection = self ._connection ,
171206 )
172207 attr_class = AttrR if ro else AttrRW
173208 attr = attr_class (
174- datatype = dtype_mapping [parameter_response ["dtype" ]],
209+ datatype = dtype_mapping [parameter_response ["dtype" ]](
210+ min = parameter_response .get ("min" , None ),
211+ max = parameter_response .get ("max" , None ),
212+ ),
175213 io_ref = ref ,
176214 initial_value = parameter_response .get ("value" , None ),
177215 )
@@ -190,16 +228,12 @@ async def initialise(self):
190228 await c .initialise ()
191229 await c .attribute_initialise ()
192230 await c .ro_int_parameter .update ()
231+ assert c .ro_int_parameter .get () == 10
232+ await c .ro_int_parameter .update ()
193233 assert c .ro_int_parameter .get () == 11
194- with pytest .raises (
195- RuntimeError , match = "Could not set int_parameter to -10, min is 0"
196- ):
197- await c .int_parameter .process (- 10 )
198234
199- with pytest .raises (
200- RuntimeError , match = "Could not set int_parameter to 101, max is 100"
201- ):
202- await c .int_parameter .process (101 )
235+ await c .int_parameter .process (20 )
236+ assert c .int_parameter .get () == 20
203237
204238
205239@pytest .mark .asyncio
0 commit comments