1515# This file represents a dummy labscript device for purposes of testing BLACS 
1616# and labscript. The device is a Intermediate Device, and can be attached to 
1717# a pseudoclock in labscript in order to test the pseudoclock behaviour 
18- # without needing a real Intermediate Device.   
19- #   
18+ # without needing a real Intermediate Device. 
19+ # 
2020# You can attach an arbitrary number of outputs to this device, however we 
21- # currently only support outputs of type AnalogOut and DigitalOut. I  would be 
21+ # currently only support outputs of type AnalogOut and DigitalOut. It  would be 
2222# easy to extend this is anyone needed further functionality. 
2323
2424
25- from  labscript_devices  import  labscript_device , BLACS_tab , BLACS_worker 
26- from  labscript  import  IntermediateDevice , DigitalOut , AnalogOut , config 
25+ from  labscript_devices  import  (
26+     BLACS_tab ,
27+     runviewer_parser ,
28+ )
29+ from  labscript  import  (
30+     IntermediateDevice ,
31+     DigitalOut ,
32+     AnalogOut ,
33+     config ,
34+     set_passed_properties ,
35+ )
36+ from  labscript_devices .NI_DAQmx .utils  import  split_conn_AO , split_conn_DO 
2737import  numpy  as  np 
38+ import  labscript_utils .h5_lock   # noqa: F401 
39+ import  h5py 
40+ 
41+ from  blacs .device_base_class  import  DeviceTab 
42+ from  blacs .tab_base_classes  import  Worker 
43+ 
2844
2945class  DummyIntermediateDevice (IntermediateDevice ):
3046
3147    description  =  'Dummy IntermediateDevice' 
32-     clock_limit  =  1e6 
3348
34-     # If this is updated, then you need to update generate_code to support whatever types you add 
49+     # If this is updated, then you need to update generate_code to support whatever 
50+     # types you add 
3551    allowed_children  =  [DigitalOut , AnalogOut ]
3652
37-     def  __init__ (self , name , parent_device , BLACS_connection = 'dummy_connection' , ** kwargs ):
38-         self .BLACS_connection  =  BLACS_connection 
53+     @set_passed_properties ( 
54+         property_names = { 
55+             "connection_table_properties" : [ 
56+                 "AO_range" , 
57+                 "num_AO" , 
58+                 "ports" , 
59+                 "clock_limit" , 
60+             ], 
61+             "device_properties" : [], 
62+         } 
63+     ) 
64+     def  __init__ (
65+         self ,
66+         name ,
67+         parent_device = None ,
68+         AO_range = [- 10.0 , 10.0 ],
69+         num_AO = 4 ,
70+         ports = {'port0' : {'num_lines' : 32 , 'supports_buffered' : True }},
71+         clock_limit = 1e6 ,
72+         ** kwargs ,
73+     ):
74+         self .AO_range  =  AO_range 
75+         self .num_AO  =  num_AO 
76+         self .ports  =  ports  if  ports  is  not None  else  {}
77+         self .clock_limit  =  clock_limit 
78+         self .BLACS_connection  =  'dummy_connection' 
3979        IntermediateDevice .__init__ (self , name , parent_device , ** kwargs )
4080
4181    def  generate_code (self , hdf5_file ):
@@ -54,43 +94,172 @@ def generate_code(self, hdf5_file):
5494                device_dtype  =  np .int8 
5595            elif  isinstance (device , AnalogOut ):
5696                device_dtype  =  np .float64 
57-             dtypes .append ((device .name , device_dtype ))
97+             dtypes .append ((device .connection , device_dtype ))
5898
5999        # create dataset 
60100        out_table  =  np .zeros (len (times ), dtype = dtypes )
61101        for  device  in  self .child_devices :
62-             out_table [device .name ][:] =  device .raw_output 
102+             out_table [device .connection ][:] =  device .raw_output 
63103
64104        group .create_dataset ('OUTPUTS' , compression = config .compression , data = out_table )
65105
66106
67- from  blacs .device_base_class  import  DeviceTab 
68- from  blacs .tab_base_classes  import  Worker 
69- 
70107@BLACS_tab  
71108class  DummyIntermediateDeviceTab (DeviceTab ):
72109    def  initialise_GUI (self ):
73-         self .create_worker ("main_worker" ,DummyIntermediateDeviceWorker ,{})
110+         # Get capabilities from connection table properties: 
111+         connection_table  =  self .settings ['connection_table' ]
112+         properties  =  connection_table .find_by_name (self .device_name ).properties 
113+ 
114+         num_AO  =  properties ['num_AO' ]
115+         # num_DO = properties['num_DO'] 
116+         ports  =  properties ['ports' ]
117+ 
118+         AO_base_units  =  'V' 
119+         if  num_AO  >  0 :
120+             AO_base_min , AO_base_max  =  properties ['AO_range' ]
121+         else :
122+             AO_base_min , AO_base_max  =  None , None 
123+         AO_base_step  =  0.1 
124+         AO_base_decimals  =  3 
125+ 
126+         # Create output objects: 
127+         AO_prop  =  {}
128+         for  i  in  range (num_AO ):
129+             AO_prop ['ao%d'  %  i ] =  {
130+                 'base_unit' : AO_base_units ,
131+                 'min' : AO_base_min ,
132+                 'max' : AO_base_max ,
133+                 'step' : AO_base_step ,
134+                 'decimals' : AO_base_decimals ,
135+             }
136+ 
137+         DO_proplist  =  []
138+         DO_hardware_names  =  []
139+         for  port_num  in  range (len (ports )):
140+             port_str  =  'port%d'  %  port_num 
141+             port_props  =  {}
142+             for  line  in  range (ports [port_str ]['num_lines' ]):
143+                 hardware_name  =  'port%d/line%d'  %  (port_num , line )
144+                 port_props [hardware_name ] =  {}
145+                 DO_hardware_names .append (hardware_name )
146+             DO_proplist .append ((port_str , port_props ))
147+ 
148+         # Create the output objects 
149+         self .create_analog_outputs (AO_prop )
150+ 
151+         # Create widgets for outputs defined so far (i.e. analog outputs only) 
152+         _ , AO_widgets , _  =  self .auto_create_widgets ()
153+ 
154+         # now create the digital output objects one port at a time 
155+         for  _ , DO_prop  in  DO_proplist :
156+             self .create_digital_outputs (DO_prop )
157+ 
158+         # Manually create the digital output widgets so they are grouped separately 
159+         DO_widgets_by_port  =  {}
160+         for  port_str , DO_prop  in  DO_proplist :
161+             DO_widgets_by_port [port_str ] =  self .create_digital_widgets (DO_prop )
162+ 
163+         # Auto place the widgets in the UI, specifying sort keys for ordering them: 
164+         widget_list  =  [("Analog outputs" , AO_widgets , split_conn_AO )]
165+         for  port_num  in  range (len (ports )):
166+             port_str  =  'port%d'  %  port_num 
167+             DO_widgets  =  DO_widgets_by_port [port_str ]
168+             name  =  "Digital outputs: %s"  %  port_str 
169+             if  ports [port_str ]['supports_buffered' ]:
170+                 name  +=  ' (buffered)' 
171+             else :
172+                 name  +=  ' (static)' 
173+             widget_list .append ((name , DO_widgets , split_conn_DO ))
174+         self .auto_place_widgets (* widget_list )
175+ 
176+         # Create and set the primary worker 
177+         self .create_worker (
178+             "main_worker" ,
179+             DummyIntermediateDeviceWorker ,
180+             {
181+                 'Vmin' : AO_base_min ,
182+                 'Vmax' : AO_base_max ,
183+                 'num_AO' : num_AO ,
184+                 'ports' : ports ,
185+                 'DO_hardware_names' : DO_hardware_names ,
186+             },
187+         )
74188        self .primary_worker  =  "main_worker" 
75189
190+         # Set the capabilities of this device 
191+         self .supports_remote_value_check (False )
192+         self .supports_smart_programming (False )
193+ 
194+ 
76195class  DummyIntermediateDeviceWorker (Worker ):
77196    def  init (self ):
78197        pass 
79198
199+     def  get_output_table (self , h5file , device_name ):
200+         """Return the OUTPUT table from the file, or None if it does not exist.""" 
201+         with  h5py .File (h5file , 'r' ) as  hdf5_file :
202+             group  =  hdf5_file ['devices' ][device_name ]
203+             try :
204+                 return  group ['OUTPUTS' ][:]
205+             except  KeyError :
206+                 return  None 
207+ 
80208    def  program_manual (self , front_panel_values ):
81-         return  front_panel_values   
209+         return  front_panel_values 
82210
83211    def  transition_to_buffered (self , device_name , h5file , initial_values , fresh ):
84-         return  initial_values 
212+         # Get the data to be programmed into the output tasks: 
213+         outputs  =  self .get_output_table (h5file , device_name )
214+ 
215+         # Collect the final values of the outputs 
216+         final_values  =  dict (zip (outputs .dtype .names , outputs [- 1 ]))
85217
86-     def  transition_to_manual (self ,abort  =  False ):
218+         return  final_values 
219+ 
220+     def  transition_to_manual (self , abort = False ):
87221        return  True 
88222
89223    def  abort_transition_to_buffered (self ):
90224        return  self .transition_to_manual (True )
91-          
225+ 
92226    def  abort_buffered (self ):
93227        return  self .transition_to_manual (True )
94228
95229    def  shutdown (self ):
96-         pass 
230+         pass 
231+ 
232+ 
233+ @runviewer_parser  
234+ class  DummyIntermediateDeviceParser (object ):
235+     def  __init__ (self , path , device ):
236+         self .path  =  path 
237+         self .name  =  device .name 
238+         self .device  =  device 
239+ 
240+     def  get_traces (self , add_trace , clock = None ):
241+         times , clock_value  =  clock [0 ], clock [1 ]
242+ 
243+         clock_indices  =  np .where ((clock_value [1 :] -  clock_value [:- 1 ]) ==  1 )[0 ] +  1 
244+         # If initial clock value is 1, then this counts as a rising edge (clock should 
245+         # be 0 before experiment) but this is not picked up by the above code. So we 
246+         # insert it! 
247+         if  clock_value [0 ] ==  1 :
248+             clock_indices  =  np .insert (clock_indices , 0 , 0 )
249+         clock_ticks  =  times [clock_indices ]
250+ 
251+         # Get the output table from the experiment shot file 
252+         with  h5py .File (self .path , 'r' ) as  hdf5_file :
253+             outputs  =  hdf5_file [f"devices/{ self .name }  ][:]
254+ 
255+         traces  =  {}
256+ 
257+         for  channel  in  outputs .dtype .names :
258+             traces [channel ] =  (clock_ticks , outputs [channel ])
259+ 
260+         for  channel_name , channel  in  self .device .child_list .items ():
261+             if  channel .parent_port  in  traces :
262+                 trace  =  traces [channel .parent_port ]
263+                 add_trace (channel_name , trace , self .name , channel .parent_port )
264+ 
265+         return  {}
0 commit comments