async def on_b_set_picowatt_config(self, sid, config): with db.connection_context(): ConfigurationParameter.overwrite_config_value( 'picowatt_delay', config['Delay']) await self.cryo_namespace.on_c_get_picowatt_delay(1) await self.cryo_namespace.config_avs47b(config)
async def on_c_got_temperatures(self, sid, temperatures): # Keep track of how many temperatures we have received self.received_temperatures += 1 # Ensure a connection to the database with db.connection_context(): # Check if we want to save the temperatures if ConfigurationParameter.read_config_value('is_saving_cryo_temperatures') and \ self.received_temperatures % ConfigurationParameter.read_config_value('save_every_n_temperatures') == 0: # Save the temperatures TemperatureDataPoint( cryo_data_point=1, t_upper_hex=temperatures['t_upper_hex'], t_lower_hex=temperatures['t_lower_hex'], t_he_pot=temperatures['t_he_pot'], t_1st_stage=temperatures['t_1st_stage'], t_2nd_stage=temperatures['t_2nd_stage'], t_inner_coil=temperatures['t_inner_coil'], t_outer_coil=temperatures['t_outer_coil'], t_switch=temperatures['t_switch'], t_he_pot_2=temperatures['t_he_pot_2'], t_still=temperatures['t_still'], t_mixing_chamber_1=temperatures['t_mixing_chamber_1'], t_mixing_chamber_2=temperatures['t_mixing_chamber_2'] ).save() # Reset the counter so we don't get very large numbers (there is no need) self.received_temperatures = 1 # Actually send the temperatures await self.browser_namespace.send_temperatures(temperatures)
async def on_c_got_step_results(self, sid, results): with db.connection_context(): # Get the datapoint associated with the step (should be generated when step is sent) datapoint = DataPoint.select().where( DataPoint.step == results['step_id']).order_by( DataPoint.created).get() # Then we save the datapoint if datapoint is not None: datapoint.save_cryo_data(results)
def on_disconnect(self, sid): with db.connection_context(): try: # Find the type and remove from the connected clients list session = Session.get(Session.sid == sid) self.remove_client_from_all_namespaces(sid, session.type) # Alert the user to the disconnect print(session.type, 'disconnected') except Session.DoesNotExist: print('disconnect ', sid)
async def on_b_get_n_points_total(self, sid): with db.connection_context(): if ExperimentConfiguration.select().count() > 0: # get the latest config from the database latest_config = ExperimentConfiguration.select().order_by( ExperimentConfiguration.id.desc()).get() # Compute the number of points taken n_points_total = ExperimentStep.select() \ .where(ExperimentStep.experiment_configuration == latest_config.id) \ .count() # Send it to the user await self.emit('b_n_points_total', n_points_total, room=sid)
async def on_mark_step_as_done(self, sid, step): # Add the id of the step to the done list step_id = step['id'] self.steps_done.append(step_id) # Check if the step is done both places if self.magnetism_namespace.is_step_done( step_id) and self.cryo_namespace.is_step_done(step_id): # Both are done, so we should mark it as done in the database with db.connection_context(): ExperimentStep.update(step_done=True).where( ExperimentStep.id == step_id).execute() # Next we should push the next step to the clients (if applicable) await self.browser_namespace.push_next_step_to_clients()
async def on_b_set_experiment_config(self, sid, data): with db.connection_context(): # First we get the session of the current user user = Session.get(Session.sid == sid) # Parse out the numbers from the client data['sr830_sensitivity'] = float(data['sr830_sensitivity']) data['sr830_frequency'] = float(data['sr830_frequency']) data['sr830_buffersize'] = int(data['sr830_buffersize']) data['n9310a_sweep_steps'] = int(data['n9310a_sweep_steps']) data['n9310a_min_frequency'] = float(data['n9310a_min_frequency']) data['n9310a_max_frequency'] = float(data['n9310a_max_frequency']) data['n9310a_min_amplitude'] = float(data['n9310a_min_amplitude']) data['n9310a_max_amplitude'] = float(data['n9310a_max_amplitude']) data['magnet_min_field'] = float(data['magnet_min_field']) data['magnet_max_field'] = float(data['magnet_max_field']) data['magnet_sweep_steps'] = int(data['magnet_sweep_steps']) data['oscope_resistor'] = 84.5 # Not configurable any more data['data_wait_before_measuring'] = float( data['data_wait_before_measuring']) data['data_points_per_measurement'] = int( data['data_points_per_measurement']) # Save the new configuration ec = ExperimentConfiguration.create(**data, created_by_id=user) ec.save() # Set all previous steps to be done ExperimentStep.update(step_done=True).where( ExperimentStep.step_done == False).execute() # Generate a new set of steps n_steps = ec.generate_steps() # Alert the user print(f'Generated {n_steps} new steps') # Push new configuration to all users await self.emit('b_latest_experiment_config', data) await self.emit('b_experiment_configuration_saved', room=sid) # Push next step to client await self.push_next_step_to_clients()
async def on_b_get_experiment_list(self, sid, data): # Grab the page we want page = data['page'] # Open connection to database with db.connection_context(): # Count number of experiments experiment_count = ExperimentConfiguration.select().count() # If no experiments exist, we send that to the client, otherwise we paginate experiments = [] if experiment_count > 0: # Paginate the experiments experiments = list(ExperimentConfiguration \ .select() \ .order_by(ExperimentConfiguration.id.desc()) \ .paginate(page, 10) \ .dicts()) # Parse the dates out # Add number of datapoints collected # Add the total number of datapoints in the run for e in experiments: e['created'] = e['created'].isoformat() # Compute the number of points taken e['n_points_taken'] = ExperimentStep.select() \ .where(ExperimentStep.experiment_configuration == e['id']) \ .where(ExperimentStep.step_done == True) \ .count() # Compute the number of points taken e['n_points_total'] = ExperimentStep.select() \ .where(ExperimentStep.experiment_configuration == e['id']) \ .count() await self.emit('b_got_experiment_list', { 'list': experiments, 'count': experiment_count, 'page': page })
async def on_idn(self, sid, data): # Figure out what type of client we have client_type = data.split('_')[0] is_old = 'Old' # Connect to the database with db.connection_context(): # Register sid with session try: # First check if the session exists, then we update Session.get(Session.idn == data).update(sid=sid).where( Session.idn == data).execute() except Session.DoesNotExist: # if it does not exist, we just create it Session.create(idn=data, sid=sid, type=client_type).save() is_old = 'New' # Add the connection to a category for easy lookup self.add_client_to_all_namespaces(sid, client_type) print(f'{is_old} client connected with idn: {data}, and sid: {sid}')
async def on_b_get_latest_experiment_config(self, sid): experiment_config = get_default_experiment_configuration() with db.connection_context(): if ExperimentConfiguration.select().count() > 0: # Use the config from the database latest_config = ExperimentConfiguration.select().order_by( ExperimentConfiguration.id.desc()).get() experiment_config['sr830_sensitivity'] = float( latest_config.sr830_sensitivity) experiment_config['sr830_frequency'] = float( latest_config.sr830_frequency) experiment_config['sr830_buffersize'] = int( latest_config.sr830_buffersize) experiment_config['n9310a_sweep_steps'] = int( latest_config.n9310a_sweep_steps) experiment_config['n9310a_min_frequency'] = float( latest_config.n9310a_min_frequency) experiment_config['n9310a_max_frequency'] = float( latest_config.n9310a_max_frequency) experiment_config['n9310a_min_amplitude'] = float( latest_config.n9310a_min_amplitude) experiment_config['n9310a_max_amplitude'] = float( latest_config.n9310a_max_amplitude) experiment_config['magnet_min_field'] = float( latest_config.magnet_min_field) experiment_config['magnet_max_field'] = float( latest_config.magnet_max_field) experiment_config['magnet_sweep_steps'] = int( latest_config.magnet_sweep_steps) experiment_config['oscope_resistor'] = float( latest_config.oscope_resistor) experiment_config['data_wait_before_measuring'] = float( latest_config.data_wait_before_measuring) experiment_config['data_points_per_measurement'] = int( latest_config.data_points_per_measurement) await self.emit('b_latest_experiment_config', experiment_config, room=sid)
async def get_next_step(self): with db.connection_context(): try: # Get the next step step = ExperimentStep.select().where( ExperimentStep.step_done == False).order_by( ExperimentStep.id).first() # Check if the step is none, and skip to the catch clause if it is if step is None: raise DoesNotExist('Step does not exist') # Check if the step has an associated datapoint if DataPoint.select().where( ExperimentStep == step).count() < 1: step.generate_datapoint() # Convert step to dict step_d = model_to_dict(step) # Set the experiment id (different from the step id) step_d['experiment_configuration_id'] = step_d[ 'experiment_configuration']['id'] # Remove datetime and experiment configuration from the dict # They are not needed in the client, and they are not directly serializable to json (due to missing datetime format) del (step_d['created']) del (step_d['experiment_configuration']) # Return the step if it exists return step_d # Check if the step even exists except DoesNotExist: # It is OK if it does not exist, we should just stop measuring print('No more steps ready') # Return None if no step exists return None
async def on_c_get_picowatt_delay(self, sid): # Grab the delay from the config database and send it to the client with db.connection_context(): picowatt_delay = ConfigurationParameter.read_config_value( 'picowatt_delay') await self.emit('c_got_picowatt_delay', picowatt_delay)
async def on_b_get_is_saving_temperatures(self, sid): with db.connection_context(): saving = ConfigurationParameter.read_config_value( 'is_saving_cryo_temperatures') await self.emit('b_got_is_saving_temperatures', saving)
async def export_data(request): # Check id exists if 'id' not in request.query: return web.Response(text='Could not find the requested id', content_type='text/html') # Grab the id config_id = request.query['id'] # Now we want to start the export # Open connection to database with db.connection_context(): # Grab the configuration first ecl = ExperimentConfiguration.select().where( ExperimentConfiguration.id == config_id).dicts() # Check if we have a result if len(ecl) > 0: # Grab the first result (There should only be one when we query by id) ec = ecl[0] # Convert date format ec['created'] = ec['created'].isoformat() # Compute the number of points taken ec['n_points_taken'] = ExperimentStep.select() \ .where(ExperimentStep.experiment_configuration == ec['id']) \ .where(ExperimentStep.step_done == True) \ .count() # Compute the number of points taken ec['n_points_total'] = ExperimentStep.select() \ .where(ExperimentStep.experiment_configuration == ec['id']) \ .count() # Add an empty array to contain steps ec['steps'] = [] # Now we're done processing the configuration # Next we get all the datapoints that were saved # We start by iterating over all the steps in the experiment for step in ExperimentStep.select().where( ExperimentStep.experiment_configuration == ec['id']).dicts(): # Convert date format step['created'] = step['created'].isoformat() # Add an empty array to contain datapoints step['datapoints'] = [] # And we iterate through all the datapoints for the step for dp in DataPoint.select().where( DataPoint.step == step['id']): # Create a dict to contain the collected information datapoint_dict = { 'id': dp.id, 'created': dp.created.isoformat(), 'magnetism_datapoints': [], 'temperature_datapoints': [], 'pressure_datapoints': [] } # Next we find the magnetism datapoint for mdp in MagnetismDataPoint.select().where( MagnetismDataPoint.datapoint == dp): # For this we find the magnetism measurements (where we actually store the data) mdps = MagnetismMeasurement.select().where( MagnetismMeasurement.magnetism_data_point == mdp) # Save it to the datapoint dict for magnetism_datapoint in list(mdps.dicts()): datapoint_dict['magnetism_datapoints'].append( magnetism_datapoint) # And we find the cryodatapoint for cdp in CryogenicsDataPoint.select().where( CryogenicsDataPoint.datapoint == dp): # Similarly we find pressure and temperature datapoints pdps = PressureDataPoint.select().where( PressureDataPoint.cryo_data_point == cdp) tdps = TemperatureDataPoint.select().where( TemperatureDataPoint.cryo_data_point == cdp) # Save them to the datapoint dict for pressure_datapoint in list(pdps.dicts()): datapoint_dict['pressure_datapoints'].append( pressure_datapoint) for temperature_datapoint in list(tdps.dicts()): # Convert date format temperature_datapoint[ 'created'] = temperature_datapoint[ 'created'].isoformat() # Append the temperature datapoint_dict['temperature_datapoints'].append( temperature_datapoint) # Save the datapoint to the step step['datapoints'].append(datapoint_dict) # Save the step to the configuration ec['steps'].append(step) # And finally we send the response data return web.json_response( headers={'Content-Disposition': f'Attachment'}, body=json.dumps(ec)) else: return web.Response(text='Attempted to export ' + str(config_id) + ' but no such config found', content_type='text/html')
async def plot_saved_temperatures(request): # Open connection to database with db.connection_context(): # Create a timedelta for the query (based on the config) period_delta = datetime.timedelta( hours=ConfigurationParameter.read_config_value('max_timeperiod')) period = datetime.datetime.today() - period_delta # Get the temperatures temperatures = TemperatureDataPoint\ .select()\ .where(TemperatureDataPoint.created > period)\ .order_by(TemperatureDataPoint.created)\ .dicts() # Determine the shape of the arrays in our output if len(temperatures) > 0: temp_shape = (len(temperatures), 1) else: temp_shape = (1, 1) # Create empty arrays to hold the data times = np.zeros(shape=temp_shape) t_upper_hex = np.zeros(shape=temp_shape) t_lower_hex = np.zeros(shape=temp_shape) t_he_pot = np.zeros(shape=temp_shape) t_1st_stage = np.zeros(shape=temp_shape) t_2nd_stage = np.zeros(shape=temp_shape) t_inner_coil = np.zeros(shape=temp_shape) t_outer_coil = np.zeros(shape=temp_shape) t_switch = np.zeros(shape=temp_shape) t_he_pot_2 = np.zeros(shape=temp_shape) t_still = np.zeros(shape=temp_shape) t_mixing_chamber_1 = np.zeros(shape=temp_shape) t_mixing_chamber_2 = np.zeros(shape=temp_shape) # Sort the data into the relevant arrays for idx, t_obj in enumerate(temperatures): times[idx] = t_obj['created'].timestamp( ) # Convert the datetime to seconds t_upper_hex[idx] = t_obj['t_upper_hex'] t_lower_hex[idx] = t_obj['t_lower_hex'] t_he_pot[idx] = t_obj['t_he_pot'] t_1st_stage[idx] = t_obj['t_1st_stage'] t_2nd_stage[idx] = t_obj['t_2nd_stage'] t_inner_coil[idx] = t_obj['t_inner_coil'] t_outer_coil[idx] = t_obj['t_outer_coil'] t_switch[idx] = t_obj['t_switch'] t_he_pot_2[idx] = t_obj['t_he_pot_2'] t_still[idx] = t_obj['t_still'] t_mixing_chamber_1[idx] = t_obj['t_mixing_chamber_1'] t_mixing_chamber_2[idx] = t_obj['t_mixing_chamber_2'] # Create the plot plt.subplots(figsize=(8, 3.5)) # Plot the data plt.plot(times, t_upper_hex, label='Upper HEx') plt.plot(times, t_lower_hex, label='Lower HEx') plt.plot(times, t_he_pot, label='He Pot') plt.plot(times, t_he_pot_2, label='He Pot CCS') plt.plot(times, t_1st_stage, label='1st stage') plt.plot(times, t_2nd_stage, label='2nd stage') plt.plot(times, t_inner_coil, label='Inner coil') plt.plot(times, t_outer_coil, label='Outer coil') plt.plot(times, t_switch, label='Switch') plt.plot(times, t_still, label='Still') plt.plot(times, t_mixing_chamber_1, label='Mixing chamber 1') plt.plot(times, t_mixing_chamber_2, label='Mixing chamber 2') # Pretty up the plot plt.xlabel('Time [seconds]') plt.ylabel('Temperature [kelvin]') plt.grid() plt.legend(loc='lower left') plt.tight_layout() # Save the plot to a buffer buffer = io.BytesIO() plt.savefig(buffer, format='png', dpi=100) plt.close() # Reset the buffer placement, and send the response buffer.seek(0) return web.Response(body=buffer, headers={ 'Content-Type': 'image/png', 'Cache-Control': 'max-age=0,no-store' })
return web.Response(body=buffer, headers={ 'Content-Type': 'image/png', 'Cache-Control': 'max-age=0,no-store' }) # Setup the http routes app.router.add_static('/static', 'static') app.router.add_get('/export', export_data) app.router.add_get('/get_plot', plot_saved_temperatures) app.router.add_get('/', index) if __name__ == '__main__': # Ensure the database tables are created with db.connection_context(): db.create_tables([ Session, ExperimentConfiguration, ExperimentStep, StationStatus, DataPoint, MagnetismDataPoint, MagnetismMeasurement, CryogenicsDataPoint, PressureDataPoint, TemperatureDataPoint, ConfigurationParameter ]) # Create a default configuration object default_config = default_config_parameters.get_default_configuration_parameters( ) # Now we load any missing keys into the database for key in default_config.keys(): ConfigurationParameter.read_config_value(key, default_config[key])
async def on_b_end_save_temperatures(self, sid): with db.connection_context(): ConfigurationParameter.overwrite_config_value( 'is_saving_cryo_temperatures', False) await self.emit('b_got_is_saving_temperatures', False)
async def got_picowatt_config(self, config): with db.connection_context(): config['Delay'] = ConfigurationParameter.read_config_value( 'picowatt_delay') await self.emit('b_got_picowatt_config', config)