class ALD_Display(Measurement): ''' - Generates the user interface layout including user input fields and \ output fields displaying ALD sensor data - Creates and updates any plots with data stored in arrays. - This module also checks whether certain conditions in the ALD system environment are met. *LoggedQuantities* in \ :class:`ALD_Recipe` are connected to indicators defined here within :meth:`setup_conditions_widget`. This Measurement module and the :class:`ALD_Display` module are interdependent and make calls to functions in the other module. These modules should be loaded by :class:`ALD_App` in the following order: 1. :class:`ALD_Recipe <ALD.ALD_recipes.ALD_recipe>` 2. :class:`ALD_Display <ALD.ALD_recipes.ALD_display>` ''' name = 'ALD_display' def __init__(self, app): Measurement.__init__(self, app) def setup(self): self.settings.New('RF_pulse_duration', dtype=int, initial=1) self.settings.New('history_length', dtype=int, initial=1e6, vmin=1, ro=True) self.settings.New('shutter_open', dtype=bool, initial=False, ro=True) self.settings.New('display_window', dtype=int, initial=1e4, vmin=1) self.setup_buffers_constants() self.resources = resources self.settings.New('save_path', dtype=str, initial=self.full_file_path, ro=False) self.ch1_scaled = self.settings.New(name='ch1_pressure', dtype=float, initial=0.0, fmt='%.4e', si=True, spinbox_decimals=6, ro=True) self.ch2_scaled = self.settings.New(name='ch2_pressure', dtype=float, initial=0.0, fmt='%.4e', si=True, spinbox_decimals=6, ro=True) self.ch3_scaled = self.settings.New(name='ch3_pressure', dtype=float, initial=0.0, fmt='%.4e', si=True, spinbox_decimals=6, ro=True) if hasattr(self.app.hardware, 'seren_hw'): self.seren = self.app.hardware.seren_hw if self.app.measurements.seren.psu_connected: print('Seren PSU Connected') else: print('Connect Seren HW component first.') if hasattr(self.app.hardware, 'ald_shutter'): self.shutter = self.app.hardware.ald_shutter else: print('Connect ALD shutter HW component first.') if hasattr(self.app.hardware, 'lovebox'): self.lovebox = self.app.hardware.lovebox if hasattr(self.app.hardware, 'mks_146_hw'): self.mks146 = self.app.hardware.mks_146_hw if hasattr(self.app.hardware, 'ni_mfc1'): self.ni_mfc1 = self.app.hardware.ni_mfc1 if hasattr(self.app.hardware, 'ni_mfc2'): self.ni_mfc2 = self.app.hardware.ni_mfc2 if hasattr(self.app.hardware, 'mks_600_hw'): self.mks600 = self.app.hardware.mks_600_hw if hasattr(self.app.hardware, 'pfeiffer_vgc_hw'): self.vgc = self.app.hardware.pfeiffer_vgc_hw if hasattr(self.app.hardware, 'vat_throttle_hw'): self.vat = self.app.hardware.vat_throttle_hw if hasattr(self.app.measurements, 'ALD_Recipe'): self.recipe = self.app.measurements.ALD_Recipe else: print('ALD_Recipe not setup') self.ui_enabled = True if self.ui_enabled: self.ui_setup() self.connect_indicators() self.ui_initial_defaults() def connect_indicators(self): '''Connects *LoggedQuantities* to UI indicators using :meth:`connect_to_widget`''' self.recipe.settings.pumping.connect_to_widget( self.pump_down_indicator) self.recipe.settings.predeposition.connect_to_widget( self.pre_deposition_indicator) self.recipe.settings.deposition.connect_to_widget( self.deposition_indicator) self.recipe.settings.vent.connect_to_widget(self.vent_indicator) self.recipe.settings.pumped.connect_to_widget(self.pumped_indicator) self.recipe.settings.gases_ready.connect_to_widget( self.gases_ready_indicator) self.recipe.settings.substrate_hot.connect_to_widget( self.substrate_indicator) self.recipe.settings.recipe_running.connect_to_widget( self.recipe_running_indicator) self.recipe.settings.recipe_completed.connect_to_widget( self.recipe_ready_indicator) self.seren.settings.RF_enable.connect_to_widget( self.plasma_on_indicator) def update_subtable(self): """ Updates subroutine table tableWidget (UI element) Called by :meth:`app.measurements.ALD_recipe.subroutine_sum` after the subroutine time table summation has been calculated. """ self.subtableModel.on_lq_updated_value() def update_table(self): """ Updates the main time table tableWidget (UI element) Called by :attr:`app.measurements.ALD_recipe.sum_times` after the time table summation has been calculated. """ self.tableModel.on_lq_updated_value() def ui_setup(self): '''Calls all functions needed to set up UI programmatically. This object is called from :meth:`setup`''' self.ui = DockArea() self.layout = QtWidgets.QVBoxLayout() self.ui.show() self.ui.setWindowTitle('ALD Control Panel') self.ui.setLayout(self.layout) self.widget_setup() self.dockArea_setup() def dockArea_setup(self): """ Creates dock objects and determines order of dockArea widget placement in UI. This function is called from :meth:`ui_setup` """ self.rf_dock = Dock('RF Settings') self.rf_dock.addWidget(self.rf_widget) self.ui.addDock(self.rf_dock) self.vgc_dock = Dock('VGC History') self.vgc_dock.addWidget(self.pressure_vgc_plot_widget) self.ui.addDock(self.vgc_dock, position='left', relativeTo=self.rf_dock) self.thermal_dock = Dock('Thermal History') self.thermal_dock.addWidget(self.thermal_widget) self.ui.addDock(self.thermal_dock, position='right', relativeTo=self.rf_dock) self.recipe_dock = Dock('Recipe Controls') self.recipe_dock.addWidget(self.recipe_control_widget) self.ui.addDock(self.recipe_dock, position='bottom') self.display_ctrl_dock = Dock('Display Controls') self.display_ctrl_dock.addWidget(self.display_control_widget) self.ui.addDock(self.display_ctrl_dock, position='bottom', relativeTo=self.recipe_dock) self.hardware_dock = Dock('Hardware') self.hardware_dock.addWidget(self.hardware_widget) self.ui.addDock(self.hardware_dock, position='top') self.conditions_dock = Dock('Conditions') self.conditions_dock.addWidget(self.conditions_widget) self.ui.addDock(self.conditions_dock, position='left', relativeTo=self.hardware_dock) self.operations_dock = Dock('Operations') self.operations_dock.addWidget(self.operations_widget) self.ui.addDock(self.operations_dock, position='left', relativeTo=self.conditions_dock) def load_ui_defaults(self): pass def save_ui_defaults(self): pass def ui_initial_defaults(self): '''Reverts the indicator arrays to their original state after deposition operations have concluded or have been interrupted. This function is called from :meth:`widget_setup` and :meth:`setup` ''' self.pump_down_button.setEnabled(False) self.pre_deposition_button.setEnabled(True) self.deposition_button.setEnabled(False) self.vent_button.setEnabled(False) self.pumped_button.setEnabled(False) self.gases_ready_button.setEnabled(False) self.plasma_on_button.setEnabled(False) self.substrate_button.setEnabled(False) self.recipe_running_button.setEnabled(False) self.recipe_complete_button.setEnabled(False) self.pre_deposition_button.clicked.connect(self.recipe.predeposition) self.deposition_button.clicked.connect(self.recipe.run_recipe) self.vent_button.clicked.connect(self.recipe.shutoff) def widget_setup(self): """ Runs collection of widget setup functions each of which creates the widget and then populates them. This function is called from :meth:`ui_setup` """ self.cb_stylesheet = ''' QCheckBox::indicator { width: 25px; height: 25px; } QCheckBox::indicator:checked { image: url(://icons//GreenLED.png); } QCheckBox::indicator:unchecked { image: url(://icons//RedLED.png); } ''' self.setup_operations_widget() self.setup_conditions_widget() self.setup_thermal_control_widget() self.setup_rf_flow_widget() self.setup_vgc_pressure_widget() self.setup_recipe_control_widget() self.setup_display_controls() self.setup_hardware_widget() self.ui_initial_defaults() def setup_conditions_widget(self): """ Creates conditions widget which is meant to provide end user with an array of LED indicators (and greyed out pushbuttons serving as labels.) which serve as indicators of desired conditions within the ALD recipe process. """ self.conditions_widget = QtWidgets.QGroupBox('Conditions Widget') self.conditions_widget.setLayout(QtWidgets.QGridLayout()) self.conditions_widget.setStyleSheet(self.cb_stylesheet) self.pumped_indicator = QtWidgets.QCheckBox() self.pumped_button = QtWidgets.QPushButton('Pumped') self.conditions_widget.layout().addWidget(self.pumped_indicator, 0, 0) self.conditions_widget.layout().addWidget(self.pumped_button, 0, 1) self.gases_ready_indicator = QtWidgets.QCheckBox() self.gases_ready_button = QtWidgets.QPushButton('Gases Ready') self.conditions_widget.layout().addWidget(self.gases_ready_indicator, 1, 0) self.conditions_widget.layout().addWidget(self.gases_ready_button, 1, 1) self.substrate_indicator = QtWidgets.QCheckBox() self.substrate_button = QtWidgets.QPushButton('Stage Temp. Ready') self.conditions_widget.layout().addWidget(self.substrate_indicator, 2, 0) self.conditions_widget.layout().addWidget(self.substrate_button, 2, 1) self.recipe_running_indicator = QtWidgets.QCheckBox() self.recipe_running_button = QtWidgets.QPushButton('Recipe Running') self.conditions_widget.layout().addWidget( self.recipe_running_indicator, 3, 0) self.conditions_widget.layout().addWidget(self.recipe_running_button, 3, 1) self.plasma_on_indicator = QtWidgets.QCheckBox() self.plasma_on_button = QtWidgets.QPushButton('Plasma On') self.conditions_widget.layout().addWidget(self.plasma_on_indicator, 4, 0) self.conditions_widget.layout().addWidget(self.plasma_on_button, 4, 1) self.recipe_complete_indicator = QtWidgets.QCheckBox() self.recipe_complete_button = QtWidgets.QPushButton('Recipe Done') self.conditions_widget.layout().addWidget( self.recipe_complete_indicator, 5, 0) self.conditions_widget.layout().addWidget(self.recipe_complete_button, 5, 1) def setup_operations_widget(self): """Creates operations widget which is meant to provide end user with push buttons which initiate specific subroutines of the ALD recipe process.""" self.operations_widget = QtWidgets.QGroupBox('Operations Widget') self.operations_widget.setLayout(QtWidgets.QGridLayout()) self.operations_widget.setStyleSheet(self.cb_stylesheet) self.pump_down_indicator = QtWidgets.QCheckBox() self.pump_down_button = QtWidgets.QPushButton('Pump Down') self.operations_widget.layout().addWidget(self.pump_down_indicator, 0, 0) self.operations_widget.layout().addWidget(self.pump_down_button, 0, 1) self.pre_deposition_indicator = QtWidgets.QCheckBox() self.pre_deposition_button = QtWidgets.QPushButton('Pre-deposition') self.operations_widget.layout().addWidget( self.pre_deposition_indicator, 1, 0) self.operations_widget.layout().addWidget(self.pre_deposition_button, 1, 1) self.deposition_indicator = QtWidgets.QCheckBox() self.deposition_button = QtWidgets.QPushButton('Deposition') self.operations_widget.layout().addWidget(self.deposition_indicator, 2, 0) self.operations_widget.layout().addWidget(self.deposition_button, 2, 1) self.vent_indicator = QtWidgets.QCheckBox() self.vent_button = QtWidgets.QPushButton('Vent') self.operations_widget.layout().addWidget(self.vent_indicator, 3, 0) self.operations_widget.layout().addWidget(self.vent_button, 3, 1) def setup_hardware_widget(self): """ Creates Hardware widget which contains the Plasma and Temperature readout subpanels and the Pressures and Flow subpanel. This enclosing widget was created solely for the purpose of organizing subpanel arrangement in UI. """ self.hardware_widget = QtWidgets.QGroupBox('Hardware Widget') self.hardware_widget.setLayout(QtWidgets.QHBoxLayout()) self.left_widget = QtWidgets.QWidget() self.left_widget.setLayout(QtWidgets.QVBoxLayout()) self.left_widget.setMinimumWidth(350) self.right_widget = QtWidgets.QGroupBox('Pressures and Flow') self.right_widget.setLayout(QtWidgets.QHBoxLayout()) self.right_widget.setMinimumHeight(250) self.temp_field_panel = QtWidgets.QGroupBox( 'Temperature Readout Panel [' + u'\u00b0' + 'C]') self.temp_field_panel.setLayout(QtWidgets.QGridLayout()) self.sp_temp_label = QtWidgets.QLabel('Set Point') self.sp_temp_field = QtWidgets.QDoubleSpinBox() self.lovebox.settings.sv_setpoint.connect_to_widget(self.sp_temp_field) self.pv_temp_label = QtWidgets.QLabel('Process Value') self.pv_temp_field = QtWidgets.QDoubleSpinBox() self.lovebox.settings.pv_temp.connect_to_widget(self.pv_temp_field) self.temp_field_panel.layout().addWidget(self.sp_temp_label, 0, 0) self.temp_field_panel.layout().addWidget(self.sp_temp_field, 0, 1) self.temp_field_panel.layout().addWidget(self.pv_temp_field, 0, 2) self.temp_field_panel.layout().addWidget(self.pv_temp_label, 0, 3) self.left_widget.layout().addWidget(self.temp_field_panel) self.hardware_widget.layout().addWidget(self.left_widget) self.hardware_widget.layout().addWidget(self.right_widget) self.setup_plasma_subpanel() self.setup_pressures_subpanel() def setup_plasma_subpanel(self): """ Creates plasma subpanel which displays information relevant to the connected RF power supply. Subpanel also includes fields allowing for user defined setpoints to be sent to the power supply software side. Creates UI elements related to the ALD RF power supply, establishes signals and slots, as well as connections between UI elements and their associated *LoggedQuantities*. """ self.plasma_panel = QtWidgets.QGroupBox('Plasma Panel [W]') self.left_widget.layout().addWidget(self.plasma_panel) self.fwd_power_input_label = QtWidgets.QLabel('FWD Power \n Input') self.fwd_power_input = QtWidgets.QDoubleSpinBox() self.fwd_power_readout_label = QtWidgets.QLabel('FWD Power \n Readout') self.fwd_power_readout = QtWidgets.QDoubleSpinBox() self.plasma_left_panel = QtWidgets.QWidget() self.plasma_left_panel.setLayout(QtWidgets.QGridLayout()) self.plasma_right_panel = QtWidgets.QWidget() self.plasma_right_panel.setLayout(QtWidgets.QGridLayout()) self.plasma_right_panel.setStyleSheet(self.cb_stylesheet) self.plasma_left_panel.layout().addWidget(self.fwd_power_input_label, 0, 0) self.plasma_left_panel.layout().addWidget(self.fwd_power_input, 0, 1) self.plasma_left_panel.layout().addWidget(self.fwd_power_readout_label, 1, 0) self.plasma_left_panel.layout().addWidget(self.fwd_power_readout, 1, 1) self.rf_indicator = QtWidgets.QCheckBox() self.rf_pushbutton = QtWidgets.QPushButton('RF ON/OFF') if hasattr(self, 'shutter'): self.seren.settings.RF_enable.connect_to_widget(self.rf_indicator) self.rf_pushbutton.clicked.connect(self.seren.RF_toggle) self.rev_power_readout_label = QtWidgets.QLabel( 'REFL Power \n Readout') self.rev_power_readout = QtWidgets.QDoubleSpinBox() self.plasma_right_panel.layout().addWidget(self.rf_indicator, 0, 1) self.plasma_right_panel.layout().addWidget(self.rf_pushbutton, 0, 0) self.plasma_right_panel.layout().addWidget(self.rev_power_readout, 1, 0) self.plasma_right_panel.layout().addWidget( self.rev_power_readout_label, 1, 1) self.seren.settings.set_forward_power.connect_to_widget( self.fwd_power_input) self.seren.settings.forward_power_readout.connect_to_widget( self.fwd_power_readout) self.seren.settings.reflected_power.connect_to_widget( self.rev_power_readout) self.plasma_panel.setLayout(QtWidgets.QHBoxLayout()) self.plasma_panel.layout().addWidget(self.plasma_left_panel) self.plasma_panel.layout().addWidget(self.plasma_right_panel) def setup_pressures_subpanel(self): """Creates pressures subpanel which display pressure sensor measurements. Subpanel includes measurement value fields and input fields which allow for user defined setpoints to be sent to pressure controllers. Creates UI elements related to ALD pressure controllers, establishes signals and slots, as well as connections between UI elements and their associated *LoggedQuantities*. """ self.flow_input_group = QtWidgets.QGroupBox('Flow Inputs') self.flow_output_group = QtWidgets.QGroupBox('Flow Outputs') self.pressures_group = QtWidgets.QGroupBox('Pressure Outputs') self.right_widget.layout().addWidget(self.flow_input_group) self.right_widget.layout().addWidget(self.flow_output_group) self.right_widget.layout().addWidget(self.pressures_group) self.flow_input_group.setStyleSheet(self.cb_stylesheet) self.flow_input_group.setLayout(QtWidgets.QGridLayout()) self.flow_output_group.setLayout(QtWidgets.QGridLayout()) self.flow_output_group.setMinimumWidth(100) self.pressures_group.setLayout(QtWidgets.QGridLayout()) self.pressures_group.setMinimumWidth(158) self.MFC1_label = QtWidgets.QLabel('MFC1 (200 sccm) Flow') self.set_MFC1_field = QtWidgets.QDoubleSpinBox() self.MFC2_label = QtWidgets.QLabel('MFC2 (20 sccm) Flow') self.set_MFC2_field = QtWidgets.QDoubleSpinBox() self.MFC3_label = QtWidgets.QLabel('MFC3 (x sccm) Flow') self.set_MFC3_field = QtWidgets.QDoubleSpinBox() self.throttle_pressure_label = QtWidgets.QLabel( 'Throttle Pressure \n [mTorr]') self.set_throttle_pressure_field = QtWidgets.QDoubleSpinBox() self.throttle_pos_label = QtWidgets.QLabel( 'Throttle Valve \n Position') self.set_throttle_pos_field = QtWidgets.QDoubleSpinBox() self.shutter_indicator = QtWidgets.QCheckBox() self.shutter_pushbutton = QtWidgets.QPushButton('Shutter Open/Close') if hasattr(self, 'shutter'): self.shutter.settings.shutter_open.connect_to_widget( self.shutter_indicator) self.shutter_pushbutton.clicked.connect( self.shutter.shutter_toggle) self.input_spacer = QtWidgets.QSpacerItem( 10, 30, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.flow_input_group.layout().addWidget(self.MFC1_label, 0, 0) self.flow_input_group.layout().addWidget(self.set_MFC1_field, 0, 1) self.flow_input_group.layout().addWidget(self.MFC2_label, 1, 0) self.flow_input_group.layout().addWidget(self.set_MFC2_field, 1, 1) self.flow_input_group.layout().addWidget(self.MFC3_label, 2, 0) self.flow_input_group.layout().addWidget(self.set_MFC3_field, 2, 1) self.flow_input_group.layout().addWidget(self.throttle_pressure_label, 3, 0) self.flow_input_group.layout().addWidget( self.set_throttle_pressure_field, 3, 1) self.flow_input_group.layout().addWidget(self.throttle_pos_label, 4, 0) self.flow_input_group.layout().addWidget(self.set_throttle_pos_field, 4, 1) self.flow_input_group.layout().addWidget(self.shutter_indicator, 5, 0) self.flow_input_group.layout().addWidget(self.shutter_pushbutton, 5, 1) self.flow_input_group.layout().addItem(self.input_spacer, 6, 0) self.read_MFC1_field = QtWidgets.QDoubleSpinBox() self.read_MFC2_field = QtWidgets.QDoubleSpinBox() self.read_MFC3_field = QtWidgets.QDoubleSpinBox() self.read_throttle_pressure_field = QtWidgets.QDoubleSpinBox() self.read_throttle_pos_field = QtWidgets.QDoubleSpinBox() self.output_spacer = QtWidgets.QSpacerItem( 10, 30, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.flow_output_group.layout().addWidget(self.read_MFC1_field, 0, 0) self.flow_output_group.layout().addWidget(self.read_MFC2_field, 1, 0) self.flow_output_group.layout().addWidget(self.read_MFC3_field, 2, 0) self.flow_output_group.layout().addWidget( self.read_throttle_pressure_field, 3, 0) self.flow_output_group.layout().addWidget(self.read_throttle_pos_field, 4, 0) self.flow_output_group.layout().addItem(self.output_spacer, 5, 0) self.ch1_readout_field = QtWidgets.QDoubleSpinBox() self.ch1_readout_label = QtWidgets.QLabel('Ch1 Pressure \n [torr]') self.ch2_readout_field = QtWidgets.QDoubleSpinBox() self.ch2_readout_label = QtWidgets.QLabel('Ch2 Pressure \n [torr]') self.ch3_readout_field = QtWidgets.QDoubleSpinBox() self.ch3_readout_label = QtWidgets.QLabel('Ch3 Pressure \n [torr]') self.pressures_group.layout().addWidget(self.ch1_readout_label, 0, 0) self.pressures_group.layout().addWidget(self.ch1_readout_field, 0, 1) self.pressures_group.layout().addWidget(self.ch2_readout_label, 1, 0) self.pressures_group.layout().addWidget(self.ch2_readout_field, 1, 1) self.pressures_group.layout().addWidget(self.ch3_readout_label, 2, 0) self.pressures_group.layout().addWidget(self.ch3_readout_field, 2, 1) self.pressures_group.layout().addItem(self.output_spacer, 3, 0) if hasattr(self, 'ni_mfc1'): self.ni_mfc1.settings.write_mfc1.connect_to_widget( self.set_MFC1_field) self.ni_mfc1.settings.read_mfc1.connect_to_widget( self.read_MFC1_field) self.ni_mfc1.settings.write_mfc2.connect_to_widget( self.set_MFC2_field) self.ni_mfc1.settings.read_mfc2.connect_to_widget( self.read_MFC2_field) if hasattr(self, 'ni_mfc2'): self.ni_mfc2.settings.write_mfc3.connect_to_widget( self.set_MFC3_field) self.ni_mfc2.settings.read_mfc3.connect_to_widget( self.read_MFC3_field) if hasattr(self, 'mks600'): self.mks600.settings.sp_set_value.connect_to_widget( self.set_throttle_pressure_field) self.mks600.settings.set_valve_position.connect_to_widget( self.set_throttle_pos_field) self.mks600.settings.pressure.connect_to_widget( self.read_throttle_pressure_field) self.mks600.settings.read_valve_position.connect_to_widget( self.read_throttle_pos_field) if hasattr(self, 'vat'): self.vat.settings.write_position.connect_to_widget( self.set_throttle_pos_field) self.vat.settings.read_position.connect_to_widget( self.read_throttle_pos_field) self.vgc.settings.ch1_pressure_scaled.connect_to_widget( self.ch1_readout_field) self.vgc.settings.ch2_pressure_scaled.connect_to_widget( self.ch2_readout_field) self.vgc.settings.ch3_pressure_scaled.connect_to_widget( self.ch3_readout_field) def setup_thermal_control_widget(self): """Creates temperature plotting widget, UI elements meant to allow the user to monitor ALD system conditions, specifically temperature, through the use of live plots.""" self.thermal_widget = QtWidgets.QGroupBox('Thermal Plot') self.thermal_widget.setLayout(QtWidgets.QVBoxLayout()) self.thermal_channels = 1 self.thermal_plot_widget = pg.GraphicsLayoutWidget() self.thermal_plot = self.thermal_plot_widget.addPlot( title='Temperature History') self.thermal_plot.showGrid(y=True) self.thermal_plot.addLegend() self.thermal_widget.layout().addWidget(self.thermal_plot_widget) self.thermal_plot_names = ['Heater Temperature'] self.thermal_plot_lines = [] for i in range(self.thermal_channels): color = pg.intColor(i) plot_line = self.thermal_plot.plot([1], pen=pg.mkPen(color, width=2), name=self.thermal_plot_names[i]) self.thermal_plot_lines.append(plot_line) self.vLine1 = pg.InfiniteLine(angle=90, movable=False) self.hLine1 = pg.InfiniteLine(angle=0, movable=False) self.thermal_plot.addItem(self.vLine1) self.thermal_plot.addItem(self.hLine1) def setup_rf_flow_widget(self): """Creates RF/MFC plotting widget. UI elements are created, which are meant to allow the user to monitor ALD system conditions through the use of live plots.""" self.rf_widget = QtWidgets.QGroupBox('RF Plot') self.layout.addWidget(self.rf_widget) self.rf_widget.setLayout(QtWidgets.QVBoxLayout()) self.rf_plot_widget = pg.GraphicsLayoutWidget() self.rf_plot = self.rf_plot_widget.addPlot( title='RF power and scaled MFC flow') self.rf_plot.showGrid(y=True) self.rf_plot.addLegend() self.rf_widget.layout().addWidget(self.rf_plot_widget) self.rf_plot_names = ['Forward', 'Reflected', 'MFC flow (scaled)'] self.rf_plot_lines = [] for i in range(self.RF_CHANS): color = pg.intColor(i) plot_line = self.rf_plot.plot([1], pen=pg.mkPen(color, width=2), name=self.rf_plot_names[i]) self.rf_plot_lines.append(plot_line) self.vLine2 = pg.InfiniteLine(angle=90, movable=False) self.rf_plot.addItem(self.vLine2) def setup_vgc_pressure_widget(self): """Creates VGC plotting widget. UI elements are created, which are meant to allow the user to monitor ALD system conditions through the use of live plots.""" self.pressure_vgc_plot_widget = QtWidgets.QGroupBox('Pressure Plot') self.layout.addWidget(self.pressure_vgc_plot_widget) self.pressure_vgc_plot_widget.setLayout(QtWidgets.QVBoxLayout()) self.vgc_plot_widget = pg.GraphicsLayoutWidget() self.vgc_plot = self.vgc_plot_widget.addPlot(title='Chamber Pressures') self.vgc_plot.setLogMode(y=True) self.vgc_plot.setYRange(-8, 2) self.vgc_plot.showGrid(y=True) self.vgc_plot.addLegend() self.pressure_vgc_plot_widget.layout().addWidget(self.vgc_plot_widget) self.vgc_plot_names = ['TKP_1', 'TKP_2', 'PKR_3'] self.vgc_plot_lines = [] for i in range(self.P_CHANS): color = pg.intColor(i) plot_line = self.vgc_plot.plot([1], pen=pg.mkPen(color, width=2), name=self.vgc_plot_names[i]) self.vgc_plot_lines.append(plot_line) self.vLine3 = pg.InfiniteLine(angle=90, movable=False) self.vgc_plot.addItem(self.vLine3) def setup_shutter_control_widget(self): """ Creates shutter control widget, UI elements related to ALD shutter controls, establishes signals and slots, as well as connections between UI elements and their associated *LoggedQuantities* """ self.shutter_control_widget = QtWidgets.QGroupBox('Shutter Controls') self.shutter_control_widget.setLayout(QtWidgets.QGridLayout()) self.shutter_control_widget.setStyleSheet(self.cb_stylesheet) self.shutter_status = QtWidgets.QCheckBox(self.shutter_control_widget) self.shutter_control_widget.layout().addWidget(self.shutter_status, 0, 0) self.shutter.settings.shutter_open.connect_to_widget( self.shutter_status) self.shaul_shutter_toggle = QtWidgets.QPushButton( 'Shaul\'s Huge Shutter Button') self.shaul_shutter_toggle.setMinimumHeight(200) font = self.shaul_shutter_toggle.font() font.setPointSize(24) self.shaul_shutter_toggle.setFont(font) self.shutter_control_widget.layout().addWidget( self.shaul_shutter_toggle, 0, 1) if hasattr(self, 'shutter'): self.shaul_shutter_toggle.clicked.connect( self.shutter.shutter_toggle) def setup_recipe_control_widget(self): """ Creates recipe control widget, UI elements related to ALD recipe settings, establishes signals and slots, as well as connections between UI elements and their associated *LoggedQuantities* The table widget consists of a hierarchy of PyQt5 classes. The structure of the table widget assumes the following form: * :class:`QWidget` * :class:`QTableView` * :class:`QTableModel` More specific to the case of the subroutine table: * :class:`QWidget` (:attr:`subroutine_table_widget`) * :class:`QTableView` (:attr:`subroutine_table`) * :class:`QTableModel` (:attr:`subtableModel`) And in the case of the main recipe table: * :class:`QWidget` (:attr:`table_widget`) * :class:`QTableView` (:attr:`pulse_table`) * :class:`QTableModel` (:attr:`tableModel`) See \ :meth:`ALD.ALD_recipes.ALD_display.setup_recipe_control_widget` \ for details. **Example of table creation using PyQt5 and ScopeFoundry:** .. highlight:: python .. code-block:: python self.table_widget = QtWidgets.QWidget() self.table_widget_layout = QtWidgets.QHBoxLayout() self.table_widget.setLayout(self.table_widget_layout) self.table_label = QtWidgets.QLabel('Table Label') self.table_widget.layout().addWidget(self.table_label) self.table = QtWidgets.QTableView() ## Optional height constraint. self.table.setMaximumHeight(65) names = ['List', 'of', 'column', 'labels'] self.tableModel = ArrayLQ_QTableModel(self.displayed_array, col_names=names) self.table.setModel(self.tableModel) self.table_widget.layout().addWidget(self.table) ### Add widget to enclosing outer widget self.containing_widget.layout().addWidget(self.table_widget) Note that :class:`ArrayLQ_QTableModel` is a ScopeFoundry function containing PyQt5 code. For simplicity, it has been included in ScopeFoundry's core framework under ndarray_interactive. """ self.recipe_control_widget = QtWidgets.QGroupBox('Recipe Controls') self.recLayout = QtWidgets.QVBoxLayout() self.recipe_control_widget.setLayout(self.recLayout) self.layout.addWidget(self.recipe_control_widget) ## Settings Widget self.settings_widget = QtWidgets.QWidget() self.settings_layout = QtWidgets.QGridLayout() self.settings_widget.setLayout(self.settings_layout) self.cycle_label = QtWidgets.QLabel('N Cycles') self.settings_widget.layout().addWidget(self.cycle_label, 0, 0) self.cycle_field = QtWidgets.QDoubleSpinBox() self.settings_widget.layout().addWidget(self.cycle_field, 0, 1) self.recipe_control_widget.layout().addWidget(self.settings_widget) self.recipe.settings.cycles.connect_to_widget(self.cycle_field) self.current_cycle_label = QtWidgets.QLabel('Cycles Completed') self.current_cycle_field = QtWidgets.QDoubleSpinBox() self.settings_widget.layout().addWidget(self.current_cycle_label, 0, 2) self.settings_widget.layout().addWidget(self.current_cycle_field, 0, 3) self.recipe.settings.cycles_completed.connect_to_widget( self.current_cycle_field) self.method_select_label = QtWidgets.QLabel('t3 Method') self.method_select_comboBox = QtWidgets.QComboBox() self.settings_widget.layout().addWidget(self.method_select_label, 1, 2) self.settings_widget.layout().addWidget(self.method_select_comboBox, 1, 3) self.recipe.settings.t3_method.connect_to_widget( self.method_select_comboBox) # Subroutine Table Widget # self.subroutine_table_widget = QtWidgets.QWidget() # self.subroutine_layout = QtWidgets.QHBoxLayout() # self.subroutine_table_widget.setLayout(self.subroutine_layout) # self.subroutine_label = QtWidgets.QLabel('Subroutine [s]') # self.subroutine_table_widget.layout().addWidget(self.subroutine_label) # # self.subroutine_table = QtWidgets.QTableView() # self.subroutine_table.setMaximumHeight(65) # sub_names = ['Cycles', 't'+u'\u2080'+' PV2', 't'+u'\u2081'+' Purge'] # self.subtableModel = ArrayLQ_QTableModel(self.recipe.settings.subroutine, col_names=sub_names) # self.subroutine_table.setModel(self.subtableModel) # self.subroutine_table_widget.layout().addWidget(self.subroutine_table) # self.recipe_control_widget.layout().addWidget(self.subroutine_table_widget) ## Main Table Widget self.table_widget = QtWidgets.QWidget() self.table_widget_layout = QtWidgets.QHBoxLayout() self.table_widget.setLayout(self.table_widget_layout) self.pulse_table = QtWidgets.QTableView() column_labels = ['t'+u'\u2080'+' Pre Purge', 't'+u'\u2081'+' (TiCl'+u'\u2084'+' PV)',\ 't'+u'\u2082'+' Purge', 't'+u'\u2083'+' (N'+u'\u2082'+'/Shutter)', \ 't'+u'\u2084'+' Purge', 't'+u'\u2085'+' Post Purge', u'\u03a3'+'t'+u'\u1d62'] row_labels = ['Time [s]', 'Position (0,1000)'] self.tableModel = ArrayLQ_QTableModel( self.recipe.settings.recipe_array, col_names=column_labels, row_names=row_labels) self.pulse_table.setModel(self.tableModel) self.table_widget.layout().addWidget(self.pulse_table) self.recipe_control_widget.layout().addWidget(self.table_widget) self.recipe_panel = QtWidgets.QWidget() self.recipe_panel_layout = QtWidgets.QGridLayout() self.recipe_panel.setLayout(self.recipe_panel_layout) self.recipe_panel.setStyleSheet(self.cb_stylesheet) self.start_button = QtWidgets.QPushButton('Start Recipe') self.start_button.clicked.connect(self.recipe.start) self.recipe_panel.layout().addWidget(self.start_button, 0, 1) self.abort_button = QtWidgets.QPushButton('Abort Recipe') self.abort_button.clicked.connect(self.recipe.interrupt) self.recipe_panel.layout().addWidget(self.abort_button, 0, 2) self.recipe_complete_subpanel = QtWidgets.QWidget() self.recipe_complete_subpanel.setLayout(QtWidgets.QHBoxLayout()) self.recipe_ready_label = QtWidgets.QLabel('Recipe Complete') self.recipe_ready_indicator = QtWidgets.QCheckBox() self.recipe_complete_subpanel.layout().addWidget( self.recipe_ready_label) self.recipe_complete_subpanel.layout().addWidget( self.recipe_ready_indicator) self.recipe_panel.layout().addWidget(self.recipe_complete_subpanel, 0, 3) self.recipe_control_widget.layout().addWidget(self.recipe_panel) self.recipe.settings.recipe_completed.connect_to_widget( self.recipe_ready_indicator) def setup_display_controls(self): """ Creates a dockArea widget containing other parameters to be set by the end user, including the length of plot time history and temperature data export path. """ self.display_control_widget = QtWidgets.QGroupBox( 'Display Control Panel') self.display_control_widget.setLayout(QtWidgets.QVBoxLayout()) self.field_panel = QtWidgets.QWidget() self.field_panel_layout = QtWidgets.QGridLayout() self.field_panel.setLayout(self.field_panel_layout) self.display_control_widget.layout().addWidget(self.field_panel) self.export_button = QtWidgets.QPushButton('Export Temperature Data') self.save_field = QtWidgets.QLineEdit('Directory') # self.save_field.setMinimumWidth(200) # self.save_field.setMaximumWidth(600) self.field_panel.layout().addWidget(self.export_button, 1, 0) self.field_panel.layout().addWidget(self.save_field, 1, 1) self.export_button.clicked.connect(self.export_to_disk) self.settings.save_path.connect_to_widget(self.save_field) plot_ui_list = ('display_window', 'history_length') self.field_panel.layout().addWidget( self.settings.New_UI(include=plot_ui_list), 2, 0) def setup_buffers_constants(self): '''Creates constants and storage arrays to be used in this module.''' home = os.path.expanduser("~") self.path = home + '\\Desktop\\' self.full_file_path = self.path + 'np_export' self.psu_connected = None self.HIST_LEN = self.settings.history_length.val self.WINDOW = self.settings.display_window.val self.RF_CHANS = 2 self.T_CHANS = 1 self.P_CHANS = 3 self.history_i = 0 self.index = 0 self.rf_history = np.zeros((self.RF_CHANS, self.HIST_LEN)) self.thermal_history = np.zeros((self.T_CHANS, self.HIST_LEN)) self.pressure_history = np.zeros((self.P_CHANS, self.HIST_LEN)) self.time_history = np.zeros((1, self.HIST_LEN), dtype='datetime64[s]') self.debug_mode = False def plot_routine(self): '''This function reads from *LoggedQuantities* and stores them in arrays. These arrays are then plotted by :meth:`update_display`''' if self.debug_mode: RF_entry = np.random.rand(self.RF_CHANS, ) T_entry = np.random.rand(self.T_CHANS, ) P_entry = np.random.rang(self.P_CHANS, ) else: RF_entry = np.array([self.seren.settings['forward_power_readout'], \ self.seren.settings['reflected_power']]) T_entry = np.array([self.lovebox.settings['pv_temp']]) P_entry = np.array([ self.vgc.settings['ch1_pressure_scaled'], self.vgc.settings['ch2_pressure_scaled'], self.vgc.settings['ch3_pressure_scaled'] ]) time_entry = datetime.datetime.now() if self.history_i < self.HIST_LEN - 1: self.index = self.history_i % self.HIST_LEN else: self.index = self.HIST_LEN - 1 self.rf_history = np.roll(self.rf_history, -1, axis=1) self.thermal_history = np.roll(self.thermal_history, -1, axis=1) self.pressure_history = np.roll(self.pressure_history, -1, axis=1) self.time_history = np.roll(self.time_history, -1, axis=1) self.rf_history[:, self.index] = RF_entry self.thermal_history[:, self.index] = T_entry self.pressure_history[:, self.index] = P_entry self.time_history[:, self.index] = time_entry self.history_i += 1 def export_to_disk(self): """ Exports numpy data arrays to disk. Writes to :attr:`self.settings.save_path` Function connected to and called by :attr:`self.export_button` """ path = self.settings['save_path'] np.save(path + '_temperature.npy', self.thermal_history) np.save(path + '_times.npy', self.time_history) def update_display(self): """ **IMPORTANT:** *Do not call this function. The core framework already does so.* Built in ScopeFoundry function is called repeatedly. Its purpose is to update UI plot objects. This particular function updates plot objects depicting stage temperature, RF power levels, and the MFC flow rate and setpoint. """ self.WINDOW = self.settings.display_window.val self.vLine1.setPos(self.WINDOW) self.vLine2.setPos(self.WINDOW) self.vLine3.setPos(self.WINDOW) lovebox_level = self.lovebox.settings['sv_setpoint'] self.hLine1.setPos(lovebox_level) lower = self.index - self.WINDOW for i in range(self.T_CHANS): if self.index >= self.WINDOW: self.thermal_plot_lines[i].setData( self.thermal_history[i, lower:self.index + 1]) else: self.thermal_plot_lines[i].setData( self.thermal_history[i, :self.index + 1]) self.vLine1.setPos(self.index) # self.vLine2.setPos(self.index) for i in range(self.RF_CHANS): if self.index >= self.WINDOW: self.rf_plot_lines[i].setData( self.rf_history[i, lower:self.index + 1]) else: self.rf_plot_lines[i].setData(self.rf_history[i, :self.index + 1]) # self.vLine1.setPos(self.index) self.vLine2.setPos(self.index) for i in range(self.P_CHANS): if self.index >= self.WINDOW: self.vgc_plot_lines[i].setData( self.pressure_history[i, lower:self.index + 1]) else: self.vgc_plot_lines[i].setData( self.pressure_history[i, :self.index + 1]) self.vLine3.setPos(self.index) def conditions_check(self): """Checks ALD system conditions. Conditions that are met appear with green LED indicators in the Conditions Widget groupBox.""" self.pumped_check() if hasattr(self, 'mks146'): self.gases_check() else: pass # print('mks146 not currently active.') self.deposition_check() self.substrate_check() self.vent_check() def run(self): dt = 0.1 while not self.interrupt_measurement_called: self.conditions_check() self.plot_routine() time.sleep(dt) def pumped_check(self): '''Checks if ALD system is properly pumped down.''' Z = 1e-3 P = self.vgc.settings['ch3_pressure_scaled'] self.recipe.settings['pumped'] = (P < Z) # if self.recipe.settings['pumped']: # self.pre_deposition_button.setEnabled(True) # self.pre_deposition_button.clicked.connect(self.recipe.predeposition) # else: # self.pre_deposition_button.setEnabled(False) def gases_check(self): '''Checks if MFC flow and system pressure conditions are ideal for deposition. Should its conditions be satisfied, its *LoggedQuantity* is updated and its respective LED indicator in the Conditions Widget groupBox will appear green.''' flow = self.mks146.settings['MFC0_flow'] condition = (0.7 <= flow) self.recipe.settings['gases_ready'] = condition def substrate_check(self): ''' Checks if stage is adequately heated. Should its condition be satisfied, its *LoggedQuantity* is updated and its respective LED indicator in the Conditions Widget groupBox will appear green. ''' T = self.lovebox.settings['sv_setpoint'] pv = self.lovebox.settings['pv_temp'] condition = (0.9 * T <= pv <= 1.1 * T) self.recipe.settings['substrate_hot'] = condition def deposition_check(self): """ Checks if deposition conditions are met. Conditions include: * System pumped * Gases ready * RF enabled * Substrate temperature hot. Button which initiates deposition is enabled only upon the satisfaction of the above 4 requirements. """ condition1 = self.recipe.settings['pumped'] condition2 = self.recipe.settings['gases_ready'] condition3 = self.seren.settings['RF_enable'] condition4 = self.recipe.settings['substrate_hot'] if condition1 and condition2 and condition3 and condition4: self.deposition_button.setEnabled(True) else: self.deposition_button.setEnabled(False) def vent_check(self): """ Checks whether predeposition and deposition stages have been completed. Should its conditions be satisfied, its *LoggedQuantity* is updated and its respective LED indicator in the Conditions Widget groupBox will appear green. """ if self.recipe.dep_complete and self.recipe.predep_complete: self.vent_button.setEnabled(True) else: self.vent_button.setEnabled(False) pass
class Pfeiffer_VGC_Measure(Measurement): """ This class creates a measurement tab in the ScopeFoundry MDI interface a widget contained in which, contains live plotting features and export features. This allows the user to not only monitor the pressure history with respect to time, but also dump the recorded arrays, :attr:`self.pressure_history` and :attr:`self.time_history` to export .npy files. """ name = "pfeiffer_vgc_measure" def __init__(self, app): Measurement.__init__(self, app) def setup(self): """Establishes *LoggedQuantities* and their initial values.""" self.settings.New('display_window', dtype=int, initial=1e4, vmin=200) self.settings.New('history_length', dtype=int, initial=1e6, vmin=1000) self.setup_buffers_constants() self.settings.New('save_path', dtype=str, initial=self.full_file_path, ro=False) self.ui_enabled = True if self.ui_enabled: self.ui_setup() if hasattr(self.app.hardware, 'pfeiffer_vgc_hw'): self.vgc = self.app.hardware['pfeiffer_vgc_hw'] else: print("Connect Pfeiffer HW component first.") def ui_setup(self): '''Calls all functions needed to set up UI programmatically. This object is called from :meth:`setup`''' self.ui = DockArea() self.layout = QtWidgets.QVBoxLayout() self.ui.show() self.ui.setLayout(self.layout) self.ui.setWindowTitle('Pfeiffer Controller Pressure History') self.widget_setup() self.dockArea_setup() def dockArea_setup(self): """ Creates dock objects and determines order of dockArea widget placement in UI. This function is called from :meth:`ui_setup` """ self.ui.addDock(name='Pressure History', position='top', widget=self.group_widget) self.ui.addDock(name='NumPy Export', position='bottom', widget=self.export_widget) def widget_setup(self): """ Runs collection of widget setup functions each of which creates the widget and then populates them. This function is called from :meth:`ui_setup` """ self.setup_plot_group_widget() self.setup_export_widget() def setup_export_widget(self): """ Creates export widget in measurement UI Called from :meth:`widget_setup` """ self.export_widget = QtWidgets.QGroupBox('Pressure Export') self.export_widget.setLayout(QtWidgets.QHBoxLayout()) self.export_widget.setMaximumHeight(100) self.export_button = QtWidgets.QPushButton('Export Pressure Data') self.save_field = QtWidgets.QLineEdit('Directory') self.export_widget.layout().addWidget(self.export_button) self.export_widget.layout().addWidget(self.save_field) self.export_button.clicked.connect(self.export_to_disk) self.settings.save_path.connect_to_widget(self.save_field) def setup_plot_group_widget(self): """ Creates plot objects in measurement UI. These are updated by :meth:`update_display` Called from :meth:`widget_setup` """ self.group_widget = QtWidgets.QGroupBox('Pfeiffer VGC Measure') self.group_widget.setLayout(QtWidgets.QVBoxLayout()) self.control_widget = QtWidgets.QWidget() self.control_widget.setLayout(QtWidgets.QHBoxLayout()) self.group_widget.layout().addWidget(self.control_widget, stretch=0) self.display_label = QtWidgets.QLabel('Display Window') self.history_label = QtWidgets.QLabel('History Length') self.display_field = QtWidgets.QLineEdit() self.history_field = QtWidgets.QLineEdit() self.control_widget.layout().addWidget(self.display_label) self.control_widget.layout().addWidget(self.display_field) self.control_widget.layout().addWidget(self.history_label) self.control_widget.layout().addWidget(self.history_field) self.settings.display_window.connect_to_widget(self.display_field) self.settings.history_length.connect_to_widget(self.history_field) self.start_button = QtWidgets.QPushButton('Start') self.stop_button = QtWidgets.QPushButton('Stop') self.control_widget.layout().addWidget(self.start_button) self.control_widget.layout().addWidget(self.stop_button) self.start_button.clicked.connect(self.start) self.stop_button.clicked.connect(self.interrupt) self.plot_widget = pg.GraphicsLayoutWidget() self.plot = self.plot_widget.addPlot(title='Chamber Pressures') self.plot.setLogMode(y=True) self.plot.setYRange(-8, 2) self.plot.showGrid(y=True) self.plot.addLegend() self.plot_names = ['TKP_1', 'TKP_2', 'PKR_3', 'MAN_4'] self.plot_lines = [] for i in range(self.NUM_CHANS): color = pg.intColor(i) plot_line = self.plot.plot([1], pen=pg.mkPen(color, width=2), name=self.plot_names[i]) self.plot_lines.append(plot_line) self.vLine = pg.InfiniteLine(angle=90, movable=False) self.plot.addItem(self.vLine) self.group_widget.layout().addWidget(self.plot_widget) def db_connect(self): """ Creates and establishes database object to be utilized in measurement routine as a method of data transcription. Currently not in use. """ self.database = SQLite_Wrapper() self.server_connected = True self.database.setup_table() self.database.setup_index() def setup_buffers_constants(self): """ Creates constants for use in live plotting features. This function reads from the following logged quantities and their initial values to create initial arrays. ================== ========== ========================================================== **LoggedQuantity** **Type** **Description** history_length int Length of data array to record pressure values over time. display_window int Section of data array to be displayed in live plot. ================== ========== ========================================================== Called once from :meth:`setup` """ home = os.path.expanduser("~") self.path = home + '\\Desktop\\' self.full_file_path = self.path + 'np' self.HIST_LEN = self.settings.history_length.val self.WINDOW = self.settings.display_window.val self.NUM_CHANS = 4 self.history_i = 0 self.index = 0 self.pressure_history = np.zeros((self.NUM_CHANS, self.HIST_LEN)) self.time_history = np.zeros((1, self.HIST_LEN), dtype='datetime64[s]') self.debug_mode = False def read_pressures(self): """ Reads data off of *LoggedQuantities* and stores the in numpy array Called from :meth:`routine` :returns: Numpy array of pressure values taken from Pfeiffer Vacuum Gauge controller. """ measurements = [] for i in (1, 2, 3): _measure = self.vgc.settings['ch{}_pressure_scaled'.format(i)] measurements.append(_measure) return np.array(measurements) def routine(self): """ Reads data off of *LoggedQuantities* and stores them in Numpy array :attr:`self.pressure_history` :attr:`self.read_pressures` is called to obtain pressures from Pfeiffer VGC. This includes for following measurements: * Pirani Gauge * Compact Full Range Gauge A direct read from logged quantity :attr:`app.hardware.mks_600_hw.settings.pressure` yields a pressure reading from the manometer installed on the MKS 600 pressure regulator. """ if self.debug_mode: readout = np.random.rand(3, ) man_readout = np.random.rand(1, ) else: readout = self.read_pressures() if hasattr(self.app.hardware, 'mks_600_hw'): man_readout = np.array( self.app.hardware['mks_600_hw'].settings['pressure']) else: man_readout = np.array(0) time_entry = datetime.datetime.now() if self.history_i < self.HIST_LEN - 1: self.index = self.history_i % self.HIST_LEN else: self.index = self.HIST_LEN - 1 self.pressure_history = np.roll(self.pressure_history, -1, axis=1) self.time_history = np.roll(self.time_history, -1, axis=1) self.pressure_history[:3, self.index] = readout self.pressure_history[3, self.index] = man_readout + 1e-5 self.history_i += 1 def export_to_disk(self): """ Exports numpy data arrays to disk. Writes to :attr:`self.settings.save_path` Function connected to and called by :attr:`self.export_button` """ path = self.settings['save_path'] np.save(path + '_pressures.npy', self.pressure_history) np.save(path + '_times.npy', self.time_history) def update_display(self): """ Updates plots with data written to Numpy array :attr:`self.pressure_history` This function is automatically called repeatedly when the measurement module is started. """ self.WINDOW = self.settings.display_window.val self.vLine.setPos(self.WINDOW) lower = self.index - self.WINDOW for i in range(self.NUM_CHANS): if self.index >= self.WINDOW: self.plot_lines[i].setData( self.pressure_history[i, lower:self.index + 1]) else: self.plot_lines[i].setData( self.pressure_history[i, :self.index + 1]) self.vLine.setPos(self.index) def reconnect_server(self): """Connects to database wrapper. Currently not in use.""" self.database.connect() def disconnect_server(self): """Disconnects from database wrapper. Currently not in use. """ self.database.closeout() def run(self): dt = 0.05 self.HIST_LEN = self.settings['history_length'] while not self.interrupt_measurement_called: self.vgc.settings.ch1_pressure.read_from_hardware() self.vgc.settings.ch2_pressure.read_from_hardware() self.vgc.settings.ch3_pressure.read_from_hardware() self.routine() time.sleep(dt)