11import asyncio
2- from dataclasses import dataclass
2+ from typing import Generic , TypeVar
33
44from bluesky .protocols import HasName , Movable
55from ophyd_async .core import (
@@ -26,18 +26,18 @@ class StopState(StrictEnum):
2626 STOP = "STOP"
2727
2828
29- class FastValveControlRequest (StrictEnum ):
29+ class ValveControlRequest (StrictEnum ):
3030 OPEN = "Open"
3131 CLOSE = "Close"
3232 RESET = "Reset"
33- ARM = "Arm"
34- DISARM = "Disarm"
3533
3634
37- class ValveControlRequest (StrictEnum ):
38- OPEN = "Open"
39- CLOSE = "Close"
40- RESET = "Reset"
35+ class FastValveControlRequest (StrictEnum ):
36+ OPEN = ValveControlRequest .OPEN .value
37+ CLOSE = ValveControlRequest .CLOSE .value
38+ RESET = ValveControlRequest .RESET .value
39+ ARM = "Arm"
40+ DISARM = "Disarm"
4141
4242
4343class ValveOpenSeqRequest (StrictEnum ):
@@ -60,138 +60,89 @@ class ValveState(StrictEnum):
6060
6161
6262class FastValveState (StrictEnum ):
63- FAULT = "Fault"
64- OPEN = "Open"
63+ FAULT = ValveState . FAULT . value
64+ OPEN = ValveState . OPEN . value
6565 OPEN_ARMED = "Open Armed"
66- CLOSED = "Closed"
66+ CLOSED = ValveState . CLOSED . value
6767 CLOSED_ARMED = "Closed Armed"
6868 NONE = "Unused"
6969
7070
71- @dataclass
72- class AllValvesControlState :
73- valve_1 : ValveControlRequest | None = None
74- valve_3 : ValveControlRequest | None = None
75- valve_5 : FastValveControlRequest | None = None
76- valve_6 : FastValveControlRequest | None = None
71+ TValveControlRequest = TypeVar (
72+ "TValveControlRequest" , bound = ValveControlRequest | FastValveControlRequest
73+ )
74+
7775
76+ class ValveControl (
77+ StandardReadable , Movable [TValveControlRequest ], Generic [TValveControlRequest ]
78+ ):
79+ def __init__ (
80+ self ,
81+ prefix : str ,
82+ valve_control_type : type [TValveControlRequest ],
83+ name : str = "" ,
84+ ):
85+ with self .add_children_as_readables ():
86+ self .control = epics_signal_rw (valve_control_type , prefix + ":CON" )
87+ self .open = epics_signal_rw (int , prefix + ":OPENSEQ" )
88+ super ().__init__ (name )
7889
79- class AllValvesControl (StandardReadable , Movable [AllValvesControlState ]):
90+ @AsyncStatus .wrap
91+ async def set (self , value : TValveControlRequest ):
92+ if value .value == "Open" :
93+ await self .open .set (ValveOpenSeqRequest .OPEN_SEQ .value )
94+ await asyncio .sleep (OPENSEQ_PULSE_LENGTH )
95+ await self .open .set (ValveOpenSeqRequest .INACTIVE .value )
96+ else :
97+ await self .control .set (value )
98+
99+
100+ class AllValvesControl (StandardReadable ):
80101 """
81- valves 2, 4, 7, 8 are not controlled by the IOC,
82- as they are under manual control.
83- fast_valves: tuple[int, ...] = (5, 6)
84- slow_valves: tuple[int, ...] = (1, 3)
102+ The default IOC for this device only controls
103+ specific valves. Other valves are under manual
104+ control.
85105 """
86106
87107 def __init__ (
88108 self ,
89109 prefix : str ,
90110 name : str = "" ,
91- fast_valves : tuple [int , ...] = (5 , 6 ),
92- slow_valves : tuple [int , ...] = (1 , 3 ),
111+ fast_valves_numbers : tuple [int , ...] = (5 , 6 ),
112+ slow_valves_numbers : tuple [int , ...] = (1 , 3 ),
93113 ) -> None :
94- self .fast_valves = fast_valves
95- self .slow_valves = slow_valves
96114 with self .add_children_as_readables ():
97115 self .valve_states : DeviceVector [SignalR [ValveState ]] = DeviceVector (
98116 {
99117 i : epics_signal_r (ValveState , f"{ prefix } V{ i } :STA" )
100- for i in self . slow_valves
118+ for i in slow_valves_numbers
101119 }
102120 )
103121 self .fast_valve_states : DeviceVector [SignalR [FastValveState ]] = (
104122 DeviceVector (
105123 {
106124 i : epics_signal_r (FastValveState , f"{ prefix } V{ i } :STA" )
107- for i in self . fast_valves
125+ for i in fast_valves_numbers
108126 }
109127 )
110128 )
111129
112- self .fast_valve_control : DeviceVector [FastValveControl ] = DeviceVector (
113- {i : FastValveControl (f"{ prefix } V{ i } " ) for i in self .fast_valves }
114- )
130+ self .fast_valve_control = {
131+ i : ValveControl (f"{ prefix } V{ i } " , FastValveControlRequest )
132+ for i in fast_valves_numbers
133+ }
115134
116- self .valve_control : DeviceVector [ValveControl ] = DeviceVector (
117- {i : ValveControl (f"{ prefix } V{ i } " ) for i in self .slow_valves }
118- )
119-
120- super ().__init__ (name )
135+ self .slow_valve_control = {
136+ i : ValveControl (f"{ prefix } V{ i } " , ValveControlRequest )
137+ for i in slow_valves_numbers
138+ }
121139
122- async def set_valve (
123- self ,
124- valve : int ,
125- value : ValveControlRequest | FastValveControlRequest ,
126- ):
127- if valve in self .slow_valves and (isinstance (value , ValveControlRequest )):
128- if value == ValveControlRequest .OPEN :
129- await self .valve_control [valve ].set (ValveOpenSeqRequest .OPEN_SEQ )
130- await asyncio .sleep (OPENSEQ_PULSE_LENGTH )
131- await self .valve_control [valve ].set (ValveOpenSeqRequest .INACTIVE )
132- else :
133- await self .valve_control [valve ].set (value )
134-
135- elif valve in self .fast_valves and (isinstance (value , FastValveControlRequest )):
136- if value == FastValveControlRequest .OPEN :
137- await self .fast_valve_control [valve ].set (ValveOpenSeqRequest .OPEN_SEQ )
138- await asyncio .sleep (OPENSEQ_PULSE_LENGTH )
139- await self .fast_valve_control [valve ].set (ValveOpenSeqRequest .INACTIVE )
140- else :
141- await self .fast_valve_control [valve ].set (value )
140+ all_valves = self .fast_valve_control | self .slow_valve_control
142141
143- @AsyncStatus .wrap
144- async def set (self , value : AllValvesControlState ):
145- await asyncio .gather (
146- * (
147- self .set_valve (int (i [- 1 ]), value )
148- for i , value in value .__dict__ .items ()
149- if value is not None
150- )
151- )
152-
153-
154- class ValveControl (
155- StandardReadable , Movable [ValveControlRequest | ValveOpenSeqRequest ]
156- ):
157- def __init__ (self , prefix : str , name : str = "" ) -> None :
158- with self .add_children_as_readables ():
159- self .close = epics_signal_rw (ValveControlRequest , prefix + ":CON" )
160- self .open = epics_signal_rw (int , prefix + ":OPENSEQ" )
142+ self .valve_control : DeviceVector [ValveControl ] = DeviceVector (all_valves )
161143
162144 super ().__init__ (name )
163145
164- def set (self , value : ValveControlRequest | ValveOpenSeqRequest ) -> AsyncStatus :
165- set_status = None
166-
167- if isinstance (value , ValveControlRequest ):
168- set_status = self .close .set (value )
169- elif isinstance (value , ValveOpenSeqRequest ):
170- set_status = self .open .set (value .value )
171-
172- return set_status
173-
174-
175- class FastValveControl (
176- StandardReadable , Movable [FastValveControlRequest | ValveOpenSeqRequest ]
177- ):
178- def __init__ (self , prefix : str , name : str = "" ) -> None :
179- with self .add_children_as_readables ():
180- self .close = epics_signal_rw (FastValveControlRequest , prefix + ":CON" )
181- self .open = epics_signal_rw (int , prefix + ":OPENSEQ" )
182-
183- super ().__init__ (name )
184-
185- def set (self , value : FastValveControlRequest | ValveOpenSeqRequest ) -> AsyncStatus :
186- set_status = None
187-
188- if isinstance (value , FastValveControlRequest ):
189- set_status = self .close .set (value )
190- elif isinstance (value , ValveOpenSeqRequest ):
191- set_status = self .open .set (value .value )
192-
193- return set_status
194-
195146
196147class Pump (StandardReadable ):
197148 def __init__ (self , prefix : str , name : str = "" ) -> None :
0 commit comments