11from __future__ import annotations
22
33from collections import Counter
4- from collections .abc import Sequence
4+ from collections .abc import Iterator , Mapping , MutableMapping , Sequence
55from copy import deepcopy
66from typing import get_type_hints
77
@@ -17,6 +17,7 @@ class BaseController(Tracer):
1717
1818 #: Attributes passed from the device at runtime.
1919 attributes : dict [str , Attribute ]
20+ root_attribute : Attribute | None = None
2021
2122 description : str | None = None
2223
@@ -36,7 +37,7 @@ def __init__(
3637 if not hasattr (self , "attributes" ):
3738 self .attributes = {}
3839 self ._path : list [str ] = path or []
39- self .__sub_controller_tree : dict [str , Controller ] = {}
40+ self .__sub_controller_tree : dict [str , BaseController ] = {}
4041
4142 self ._bind_attrs ()
4243
@@ -144,7 +145,7 @@ def add_attribute(self, name, attribute: Attribute):
144145 self .attributes [name ] = attribute
145146 super ().__setattr__ (name , attribute )
146147
147- def add_sub_controller (self , name : str , sub_controller : Controller ):
148+ def add_sub_controller (self , name : str , sub_controller : BaseController ):
148149 if name in self .__sub_controller_tree .keys ():
149150 raise ValueError (
150151 f"Cannot add sub controller { sub_controller } . "
@@ -166,7 +167,7 @@ def add_sub_controller(self, name: str, sub_controller: Controller):
166167 self .attributes [name ] = sub_controller .root_attribute
167168
168169 @property
169- def sub_controllers (self ) -> dict [str , Controller ]:
170+ def sub_controllers (self ) -> dict [str , BaseController ]:
170171 return self .__sub_controller_tree
171172
172173 def __repr__ (self ):
@@ -194,17 +195,73 @@ class Controller(BaseController):
194195 such as generating a UI or creating parameters for a control system.
195196 """
196197
197- root_attribute : Attribute | None = None
198-
199198 def __init__ (
200199 self ,
201200 description : str | None = None ,
202201 ios : Sequence [AttributeIO [T , AttributeIORefT ]] | None = None ,
203202 ) -> None :
204203 super ().__init__ (description = description , ios = ios )
205204
205+ def add_sub_controller (self , name : str , sub_controller : BaseController ):
206+ if name .isdigit ():
207+ raise ValueError (
208+ f"Cannot add sub controller { name } . "
209+ "Numeric-only names are not allowed; use ControllerVector instead"
210+ )
211+ return super ().add_sub_controller (name , sub_controller )
212+
206213 async def connect (self ) -> None :
207214 pass
208215
209216 async def disconnect (self ) -> None :
210217 pass
218+
219+
220+ class ControllerVector (MutableMapping [int , Controller ], BaseController ):
221+ """A controller with a collection of identical sub controllers distinguished
222+ by a numeric value"""
223+
224+ def __init__ (
225+ self ,
226+ children : Mapping [int , Controller ],
227+ description : str | None = None ,
228+ ios : Sequence [AttributeIO [T , AttributeIORefT ]] | None = None ,
229+ ) -> None :
230+ super ().__init__ (description = description , ios = ios )
231+ self ._children : dict [int , Controller ] = {}
232+ for index , child in children .items ():
233+ self [index ] = child
234+
235+ def add_sub_controller (self , name : str , sub_controller : BaseController ):
236+ raise NotImplementedError (
237+ "Cannot add named sub controller to ControllerVector. "
238+ "Use __setitem__ instead, for indexed sub controllers. "
239+ "E.g., vector[1] = Controller()"
240+ )
241+
242+ def __getitem__ (self , key : int ) -> Controller :
243+ try :
244+ return self ._children [key ]
245+ except KeyError as exception :
246+ raise KeyError (
247+ f"ControllerVector does not have Controller with key { key } "
248+ ) from exception
249+
250+ def __setitem__ (self , key : int , value : Controller ) -> None :
251+ if not isinstance (key , int ):
252+ msg = f"Expected int, got { key } "
253+ raise TypeError (msg )
254+ if not isinstance (value , Controller ):
255+ msg = f"Expected Controller, got { value } "
256+ raise TypeError (msg )
257+ self ._children [key ] = value
258+ super ().add_sub_controller (str (key ), value )
259+
260+ def __delitem__ (self , key : int ) -> None :
261+ raise NotImplementedError ("Cannot delete sub controller from ControllerVector." )
262+
263+ def __iter__ (self ) -> Iterator [int ]:
264+ yield from self ._children
265+
266+ def __len__ (self ) -> int :
267+ return len (self ._children )
0 commit comments