22
33import asyncio
44from collections .abc import Callable
5- from enum import Enum
6- from typing import Any , Generic
5+ from typing import Generic
76
8- import fastcs
9-
10- from .datatypes import ATTRIBUTE_TYPES , AttrCallback , DataType , T
7+ from .attribute_io_ref import AttributeIORefT
8+ from .datatypes import ATTRIBUTE_TYPES , AttrSetCallback , AttrUpdateCallback , DataType , T
119
1210ONCE = float ("inf" )
1311"""Special value to indicate that an attribute should be updated once on start up."""
1412
1513
16- class AttrMode (Enum ):
17- """Access mode of an ``Attribute``."""
18-
19- READ = 1
20- WRITE = 2
21- READ_WRITE = 3
22-
23-
24- class _BaseAttrHandler :
25- async def initialise (self , controller : fastcs .controller .BaseController ) -> None :
26- pass
27-
28-
29- class AttrHandlerW (_BaseAttrHandler ):
30- """Protocol for setting the value of an ``Attribute``."""
31-
32- async def put (self , attr : AttrW [T ], value : T ) -> None :
33- pass
34-
35-
36- class AttrHandlerR (_BaseAttrHandler ):
37- """Protocol for updating the cached readback value of an ``Attribute``."""
38-
39- # If update period is None then the attribute will not be updated as a task.
40- update_period : float | None = None
41-
42- async def update (self , attr : AttrR [T ]) -> None :
43- pass
44-
45-
46- class AttrHandlerRW (AttrHandlerR , AttrHandlerW ):
47- """Protocol encapsulating both ``AttrHandlerR`` and ``AttHandlerW``."""
48-
49- pass
50-
51-
52- class SimpleAttrHandler (AttrHandlerRW ):
53- """Handler for internal parameters"""
54-
55- async def put (self , attr : AttrW [T ], value : T ) -> None :
56- await attr .update_display_without_process (value )
57-
58- if isinstance (attr , AttrRW ):
59- await attr .set (value )
60-
61- async def update (self , attr : AttrR ) -> None :
62- raise RuntimeError ("SimpleHandler cannot update" )
63-
64-
65- class Attribute (Generic [T ]):
14+ class Attribute (Generic [T , AttributeIORefT ]):
6615 """Base FastCS attribute.
6716
6817 Instances of this class added to a ``Controller`` will be used by the backend.
@@ -71,26 +20,33 @@ class Attribute(Generic[T]):
7120 def __init__ (
7221 self ,
7322 datatype : DataType [T ],
74- access_mode : AttrMode ,
23+ io_ref : AttributeIORefT | None = None ,
7524 group : str | None = None ,
76- handler : Any = None ,
7725 description : str | None = None ,
7826 ) -> None :
7927 assert issubclass (datatype .dtype , ATTRIBUTE_TYPES ), (
8028 f"Attr type must be one of { ATTRIBUTE_TYPES } , "
8129 "received type {datatype.dtype}"
8230 )
31+ self ._io_ref = io_ref
8332 self ._datatype : DataType [T ] = datatype
84- self ._access_mode : AttrMode = access_mode
8533 self ._group = group
86- self ._handler = handler
8734 self .enabled = True
8835 self .description = description
8936
9037 # A callback to use when setting the datatype to a different value, for example
9138 # changing the units on an int. This should be implemented in the backend.
9239 self ._update_datatype_callbacks : list [Callable [[DataType [T ]], None ]] = []
9340
41+ @property
42+ def io_ref (self ) -> AttributeIORefT :
43+ if self ._io_ref is None :
44+ raise RuntimeError (f"{ self } has no AttributeIORef" )
45+ return self ._io_ref
46+
47+ def has_io_ref (self ):
48+ return self ._io_ref is not None
49+
9450 @property
9551 def datatype (self ) -> DataType [T ]:
9652 return self ._datatype
@@ -99,18 +55,10 @@ def datatype(self) -> DataType[T]:
9955 def dtype (self ) -> type [T ]:
10056 return self ._datatype .dtype
10157
102- @property
103- def access_mode (self ) -> AttrMode :
104- return self ._access_mode
105-
10658 @property
10759 def group (self ) -> str | None :
10860 return self ._group
10961
110- async def initialise (self , controller : fastcs .controller .BaseController ) -> None :
111- if self ._handler is not None :
112- await self ._handler .initialise (controller )
113-
11462 def add_update_datatype_callback (
11563 self , callback : Callable [[DataType [T ]], None ]
11664 ) -> None :
@@ -126,71 +74,71 @@ def update_datatype(self, datatype: DataType[T]) -> None:
12674 callback (datatype )
12775
12876
129- class AttrR (Attribute [T ]):
77+ class AttrR (Attribute [T , AttributeIORefT ]):
13078 """A read-only ``Attribute``."""
13179
13280 def __init__ (
13381 self ,
13482 datatype : DataType [T ],
135- access_mode = AttrMode . READ ,
83+ io_ref : AttributeIORefT | None = None ,
13684 group : str | None = None ,
137- handler : AttrHandlerR | None = None ,
13885 initial_value : T | None = None ,
13986 description : str | None = None ,
14087 ) -> None :
14188 super ().__init__ (
14289 datatype , # type: ignore
143- access_mode ,
90+ io_ref ,
14491 group ,
145- handler ,
14692 description = description ,
14793 )
14894 self ._value : T = (
14995 datatype .initial_value if initial_value is None else initial_value
15096 )
151- self ._update_callbacks : list [AttrCallback [T ]] | None = None
152- self ._updater = handler
97+ self ._on_set_callbacks : list [AttrSetCallback [T ]] | None = None
98+ self ._on_update_callbacks : list [ AttrUpdateCallback ] | None = None
15399
154100 def get (self ) -> T :
155101 return self ._value
156102
157103 async def set (self , value : T ) -> None :
158104 self ._value = self ._datatype .validate (value )
159105
160- if self ._update_callbacks is not None :
161- await asyncio .gather (* [cb (self ._value ) for cb in self ._update_callbacks ])
106+ if self ._on_set_callbacks is not None :
107+ await asyncio .gather (* [cb (self ._value ) for cb in self ._on_set_callbacks ])
162108
163- def add_update_callback (self , callback : AttrCallback [T ]) -> None :
164- if self ._update_callbacks is None :
165- self ._update_callbacks = []
166- self ._update_callbacks .append (callback )
109+ def add_set_callback (self , callback : AttrSetCallback [T ]) -> None :
110+ if self ._on_set_callbacks is None :
111+ self ._on_set_callbacks = []
112+ self ._on_set_callbacks .append (callback )
167113
168- @property
169- def updater (self ) -> AttrHandlerR | None :
170- return self ._updater
114+ def add_update_callback (self , callback : AttrUpdateCallback ):
115+ if self ._on_update_callbacks is None :
116+ self ._on_update_callbacks = []
117+ self ._on_update_callbacks .append (callback )
171118
119+ async def update (self ):
120+ if self ._on_update_callbacks is not None :
121+ await asyncio .gather (* [cb () for cb in self ._on_update_callbacks ])
172122
173- class AttrW (Attribute [T ]):
123+
124+ class AttrW (Attribute [T , AttributeIORefT ]):
174125 """A write-only ``Attribute``."""
175126
176127 def __init__ (
177128 self ,
178129 datatype : DataType [T ],
179- access_mode = AttrMode . WRITE ,
130+ io_ref : AttributeIORefT | None = None ,
180131 group : str | None = None ,
181- handler : AttrHandlerW | None = None ,
182132 description : str | None = None ,
183133 ) -> None :
184134 super ().__init__ (
185135 datatype , # type: ignore
186- access_mode ,
136+ io_ref ,
187137 group ,
188- handler ,
189138 description = description ,
190139 )
191- self ._process_callbacks : list [AttrCallback [T ]] | None = None
192- self ._write_display_callbacks : list [AttrCallback [T ]] | None = None
193- self ._setter = handler
140+ self ._process_callbacks : list [AttrSetCallback [T ]] | None = None
141+ self ._write_display_callbacks : list [AttrSetCallback [T ]] | None = None
194142
195143 async def process (self , value : T ) -> None :
196144 await self .process_without_display_update (value )
@@ -206,45 +154,23 @@ async def update_display_without_process(self, value: T) -> None:
206154 if self ._write_display_callbacks :
207155 await asyncio .gather (* [cb (value ) for cb in self ._write_display_callbacks ])
208156
209- def add_process_callback (self , callback : AttrCallback [T ]) -> None :
157+ def add_process_callback (self , callback : AttrSetCallback [T ]) -> None :
210158 if self ._process_callbacks is None :
211159 self ._process_callbacks = []
212160 self ._process_callbacks .append (callback )
213161
214162 def has_process_callback (self ) -> bool :
215163 return bool (self ._process_callbacks )
216164
217- def add_write_display_callback (self , callback : AttrCallback [T ]) -> None :
165+ def add_write_display_callback (self , callback : AttrSetCallback [T ]) -> None :
218166 if self ._write_display_callbacks is None :
219167 self ._write_display_callbacks = []
220168 self ._write_display_callbacks .append (callback )
221169
222- @property
223- def sender (self ) -> AttrHandlerW | None :
224- return self ._setter
225-
226170
227- class AttrRW (AttrR [T ], AttrW [T ]):
171+ class AttrRW (AttrR [T , AttributeIORefT ], AttrW [T , AttributeIORefT ]):
228172 """A read-write ``Attribute``."""
229173
230- def __init__ (
231- self ,
232- datatype : DataType [T ],
233- access_mode = AttrMode .READ_WRITE ,
234- group : str | None = None ,
235- handler : AttrHandlerRW | None = None ,
236- initial_value : T | None = None ,
237- description : str | None = None ,
238- ) -> None :
239- super ().__init__ (
240- datatype , # type: ignore
241- access_mode ,
242- group = group ,
243- handler = handler if handler else SimpleAttrHandler (),
244- initial_value = initial_value ,
245- description = description ,
246- )
247-
248174 async def process (self , value : T ) -> None :
249175 await self .set (value )
250176
0 commit comments