Exemplo n.º 1
0
    def StepFields(self):
        """
        Dynamically generate step fields to support dynamic default values.


        .. versionadded:: 2.25

        .. versionchanged:: 2.28.2
            Set explicit field titles to prevent case mangling for protocol
            grid column titles.
        """
        app_values = self.get_app_values()
        if not app_values:
            app_values = self.get_default_app_options()
            self.set_app_values(app_values)

        fields = Form.of(Float.named('Duration (s)')
                         .using(default=app_values['default_duration'],
                                optional=True,
                                validators=[ValueAtLeast(minimum=0)]),
                         Float.named('Voltage (V)')
                         .using(default=app_values['default_voltage'],
                                optional=True,
                                validators=[ValueAtLeast(minimum=0)]),
                         Float.named('Frequency (Hz)')
                         .using(default=app_values['default_frequency'],
                                optional=True,
                                validators=[ValueAtLeast(minimum=0)]))

        # Set explicit field title to prevent case mangling for protocol grid
        # column titles.
        for field in fields.field_schema:
            field.properties['title'] = field.name
        return fields
Exemplo n.º 2
0
 def AppFields(self):
     '''
     .. versionadded:: 2.25
     '''
     return Form.of(
         Float.named('default_duration').using(default=1., optional=True),
         Float.named('default_voltage').using(default=100, optional=True),
         Float.named('default_frequency').using(default=10e3,
                                                optional=True))
Exemplo n.º 3
0
    def AppFields(self):
        serial_ports = list(get_serial_ports())
        if len(serial_ports):
            default_port = serial_ports[0]
        else:
            default_port = None

        return Form.of(
            Enum.named('serial_port').using(
                default=default_port, optional=True).valued(*serial_ports),
            Float.named('default_duration').using(default=1000, optional=True),
            Float.named('default_voltage').using(default=80, optional=True),
            Float.named('default_frequency').using(default=10e3,
                                                   optional=True),
            Boolean.named('Auto-run diagnostic tests').using(default=True,
                                                             optional=True))
Exemplo n.º 4
0
 def get_step_form_class(self):
     """
     Override to set default values based on their corresponding app options.
     """
     app_values = self.get_app_values()
     return Form.of(
         Integer.named('duration').using(
             default=app_values['default_duration'],
             optional=True,
             validators=[ValueAtLeast(minimum=0)]),
         Float.named('voltage').using(
             default=app_values['default_voltage'],
             optional=True,
             validators=[ValueAtLeast(minimum=0), max_voltage]),
         Float.named('frequency').using(
             default=app_values['default_frequency'],
             optional=True,
             validators=[ValueAtLeast(minimum=0), check_frequency]))
Exemplo n.º 5
0
def dict_to_form(dict_):
    """
    Generate a flatland form based on a pandas Series.
    """
    from flatland import Boolean, Form, String, Integer, Float

    def is_float(v):
        try:
            return (float(str(v)), True)[1]
        except (ValueError, TypeError):
            return False

    def is_int(v):
        try:
            return (int(str(v)), True)[1]
        except (ValueError, TypeError):
            return False

    def is_bool(v):
        return v in (True, False)

    schema_entries = []
    for k, v in dict_.iteritems():
        if is_int(v):
            schema_entries.append(
                Integer.named(k).using(default=v, optional=True))
        elif is_float(v):
            schema_entries.append(
                Float.named(k).using(default=v, optional=True))
        elif is_bool(v):
            schema_entries.append(
                Boolean.named(k).using(default=v, optional=True))
        elif type(v) == str:
            schema_entries.append(
                String.named(k).using(default=v, optional=True))

    return Form.of(*schema_entries)
Exemplo n.º 6
0
def dict_to_form(dict):
    '''
    Generate a flatland form based on a pandas Series.
    '''
    from flatland import Boolean, Form, String, Integer, Float

    def is_float(v):
        try:
            return (float(str(v)), True)[1]
        except (ValueError, TypeError):
            return False

    def is_int(v):
        try:
            return (int(str(v)), True)[1]
        except (ValueError, TypeError):
            return False

    def is_bool(v):
        return v in (True, False)

    schema_entries = []
    for k, v in dict.iteritems():
        if is_int(v):
            schema_entries.append(Integer.named(k).using(default=v,
                                                         optional=True))
        elif is_float(v):
            schema_entries.append(Float.named(k).using(default=v,
                                                       optional=True))
        elif is_bool(v):
            schema_entries.append(Boolean.named(k).using(default=v,
                                                         optional=True))
        elif type(v) == str:
            schema_entries.append(String.named(k).using(default=v,
                                                        optional=True))

    return Form.of(*schema_entries)
Exemplo n.º 7
0
def get_channel_sweep_parameters(voltage=100, frequency=10e3, channels=None,
                                 parent=None):
    '''
    Show dialog to select parameters for a sweep across a selected set of
    channels.

    Args
    ----

        voltage (int) : Default actuation voltage.
        frequency (int) : Default actuation frequency.
        channels (pandas.Series) : Default channels selection, encoded as
            boolean array indexed by channel number, where `True` values
            indicate selected channel(s).
        parent (gtk.Window) : If not `None`, parent window for dialog.  For
            example, display dialog at position relative to the parent window.

    Returns
    -------

        (dict) : Values collected from widgets with the following keys:
            `'frequency'`, `voltage'`, and (optionally) `'channels'`.
    '''
    # Create a form view containing widgets to set the waveform attributes
    # (i.e., voltage and frequency).
    form = Form.of(Float.named('voltage')
                   .using(default=voltage,
                          validators=[ValueAtLeast(minimum=0)]),
                   Float.named('frequency')
                   .using(default=frequency,
                          validators=[ValueAtLeast(minimum=1)]))
    form_view = create_form_view(form)

    # If default channel selection was provided, create a treeview with one row
    # per channel, and a checkbox in each row to mark the selection status of
    # the corresponding channel.
    if channels is not None:
        df_channel_select = pd.DataFrame(channels.index, columns=['channel'])
        df_channel_select.insert(1, 'select', channels.values)
        view_channels = ListSelect(df_channel_select)

    # Create dialog window.
    dialog = gtk.Dialog(title='Channel sweep parameters',
                        buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
                                 gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))

    # Add waveform widgets to dialog window.
    frame_waveform = gtk.Frame('Waveform properties')
    frame_waveform.add(form_view.widget)
    dialog.vbox.pack_start(child=frame_waveform, expand=False, fill=False,
                           padding=5)

    # Add channel selection widgets to dialog window.
    if channels is not None:
        frame_channels = gtk.Frame('Select channels to sweep')
        frame_channels.add(view_channels.widget)
        dialog.vbox.pack_start(child=frame_channels, expand=True, fill=True,
                               padding=5)

    # Mark all widgets as visible.
    dialog.vbox.show_all()

    if parent is not None:
        dialog.window.set_transient_for(parent)

    response = dialog.run()
    dialog.destroy()

    if response != gtk.RESPONSE_OK:
        raise RuntimeError('Dialog cancelled.')

    # Collection waveform and channel selection values from dialog.
    form_values = {name: f.element.value
                   for name, f in form_view.form.fields.items()}

    if channels is not None:
        form_values['channels'] = (df_channel_select
                                   .loc[df_channel_select['select'],
                                        'channel'].values)

    return form_values
Exemplo n.º 8
0
class DropBotDxPlugin(Plugin, StepOptionsController, AppDataController):
    """
    This class is automatically registered with the PluginManager.
    """
    implements(IPlugin)
    implements(IWaveformGenerator)

    serial_ports_ = [port for port in get_serial_ports()]
    if len(serial_ports_):
        default_port_ = serial_ports_[0]
    else:
        default_port_ = None

    AppFields = Form.of(
        Enum.named('serial_port').using(default=default_port_,
                                        optional=True).valued(*serial_ports_),
        Float.named('default_duration').using(default=1000, optional=True),
        Float.named('default_voltage').using(default=80, optional=True),
        Float.named('default_frequency').using(default=10e3, optional=True),
    )

    version = get_plugin_info(path(__file__).parent).version

    @property
    def StepFields(self):
        """
        Expose StepFields as a property to avoid breaking code that accesses
        the StepFields member (vs through the get_step_form_class method).
        """
        return self.get_step_form_class()

    def __init__(self):
        self.control_board = None
        self.name = get_plugin_info(path(__file__).parent).plugin_name
        self.connection_status = "Not connected"
        self.current_frequency = None
        self.timeout_id = None
        self.channel_states = pd.Series()
        self.plugin = None
        self.plugin_timeout_id = None

    def get_step_form_class(self):
        """
        Override to set default values based on their corresponding app options.
        """
        app = get_app()
        app_values = self.get_app_values()
        return Form.of(
            Integer.named('duration').using(
                default=app_values['default_duration'],
                optional=True,
                validators=[
                    ValueAtLeast(minimum=0),
                ]),
            Float.named('voltage').using(
                default=app_values['default_voltage'],
                optional=True,
                validators=[ValueAtLeast(minimum=0), max_voltage]),
            Float.named('frequency').using(
                default=app_values['default_frequency'],
                optional=True,
                validators=[ValueAtLeast(minimum=0), check_frequency]),
        )

    def update_channel_states(self, channel_states):
        # Update locally cached channel states with new modified states.
        try:
            self.channel_states = channel_states.combine_first(
                self.channel_states)
        except ValueError:
            logging.info('channel_states: %s', channel_states)
            logging.info('self.channel_states: %s', self.channel_states)
            logging.info('', exc_info=True)
        else:
            app = get_app()
            connected = self.control_board != None
            if connected and (app.realtime_mode or app.running):
                self.on_step_run()

    def cleanup_plugin(self):
        if self.plugin_timeout_id is not None:
            gobject.source_remove(self.plugin_timeout_id)
        if self.plugin is not None:
            self.plugin = None

    def on_plugin_enable(self):
        super(DropBotDxPlugin, self).on_plugin_enable()

        self.cleanup_plugin()
        # Initialize 0MQ hub plugin and subscribe to hub messages.
        self.plugin = DmfZmqPlugin(self,
                                   self.name,
                                   get_hub_uri(),
                                   subscribe_options={zmq.SUBSCRIBE: ''})
        # Initialize sockets.
        self.plugin.reset()

        # Periodically process outstanding message received on plugin sockets.
        self.plugin_timeout_id = gtk.timeout_add(10, self.plugin.check_sockets)

        self.check_device_name_and_version()
        if get_app().protocol:
            self.on_step_run()
            self._update_protocol_grid()

    def on_plugin_disable(self):
        self.cleanup_plugin()
        if get_app().protocol:
            self.on_step_run()
            self._update_protocol_grid()

    def on_app_exit(self):
        """
        Handler called just before the Microdrop application exits.
        """
        self.cleanup_plugin()
        try:
            self.control_board.hv_output_enabled = False
        except:  # ignore any exceptions (e.g., if the board is not connected)
            pass

    def on_protocol_swapped(self, old_protocol, protocol):
        self._update_protocol_grid()

    def _update_protocol_grid(self):
        pgc = get_service_instance(ProtocolGridController, env='microdrop')
        if pgc.enabled_fields:
            pgc.update_grid()

    def on_app_options_changed(self, plugin_name):
        app = get_app()
        if plugin_name == self.name:
            app_values = self.get_app_values()
            reconnect = False

            if self.control_board:
                for k, v in app_values.items():
                    if k == 'serial_port' and self.control_board.port != v:
                        reconnect = True

            if reconnect:
                self.connect()

            self._update_protocol_grid()
        elif plugin_name == app.name:
            # Turn off all electrodes if we're not in realtime mode and not
            # running a protocol.
            if (self.control_board and not app.realtime_mode
                    and not app.running):
                logger.info('Turning off all electrodes.')
                self.control_board.hv_output_enabled = False

    def connect(self):
        """
        Try to connect to the control board at the default serial port selected
        in the Microdrop application options.

        If unsuccessful, try to connect to the control board on any available
        serial port, one-by-one.
        """
        self.current_frequency = None
        if len(DropBotDxPlugin.serial_ports_):
            app_values = self.get_app_values()
            # try to connect to the last successful port
            try:
                self.control_board = SerialProxy(
                    port=str(app_values['serial_port']))
            except:
                logger.warning(
                    'Could not connect to control board on port %s.'
                    ' Checking other ports...',
                    app_values['serial_port'],
                    exc_info=True)
                self.control_board = SerialProxy()
            self.control_board.initialize_switching_boards()
            app_values['serial_port'] = self.control_board.port
            self.set_app_values(app_values)
        else:
            raise Exception("No serial ports available.")

    def check_device_name_and_version(self):
        """
        Check to see if:

         a) The connected device is a OpenDrop
         b) The device firmware matches the host driver API version

        In the case where the device firmware version does not match, display a
        dialog offering to flash the device with the firmware version that
        matches the host driver API version.
        """
        try:
            self.connect()
            name = self.control_board.properties['package_name']
            if name != self.control_board.host_package_name:
                raise Exception("Device is not a DropBot DX")

            host_software_version = self.control_board.host_software_version
            remote_software_version = self.control_board.remote_software_version

            # Reflash the firmware if it is not the right version.
            if host_software_version != remote_software_version:
                response = yesno(
                    "The DropBot DX firmware version (%s) "
                    "does not match the driver version (%s). "
                    "Update firmware?" %
                    (remote_software_version, host_software_version))
                if response == gtk.RESPONSE_YES:
                    self.on_flash_firmware()
        except Exception, why:
            logger.warning("%s" % why)

        self.update_connection_status()
Exemplo n.º 9
0
    def on_edit_calibration(self, widget=None, data=None):
        if not self.control_board.connected():
            logging.error("A control board must be connected in order to "
                          "edit calibration settings.")
            return

        hardware_version = utility.Version.fromstring(
            self.control_board.hardware_version())

        schema_entries = []
        settings = {}
        settings['amplifier_gain'] = self.control_board.amplifier_gain()
        schema_entries.append(
            Float.named('amplifier_gain').using(
                default=settings['amplifier_gain'],
                optional=True, validators=[ValueAtLeast(minimum=0.01), ]),
        )
        settings['auto_adjust_amplifier_gain'] = self.control_board \
            .auto_adjust_amplifier_gain()
        schema_entries.append(
            Boolean.named('auto_adjust_amplifier_gain').using(
                default=settings['auto_adjust_amplifier_gain'], optional=True),
        )
        settings['voltage_tolerance'] = \
            self.control_board.voltage_tolerance();
        schema_entries.append(
            Float.named('voltage_tolerance').using(
                default=settings['voltage_tolerance'], optional=True,
                validators=[ValueAtLeast(minimum=0),]),
        )
        
        if hardware_version.major == 1:        
            settings['WAVEOUT_GAIN_1'] = self.control_board \
                .eeprom_read(self.control_board.EEPROM_WAVEOUT_GAIN_1_ADDRESS)
            schema_entries.append(
                Integer.named('WAVEOUT_GAIN_1').using(
                    default=settings['WAVEOUT_GAIN_1'], optional=True,
                    validators=[ValueAtLeast(minimum=0),
                                ValueAtMost(maximum=255),]),
            )
            settings['VGND'] = self.control_board \
                .eeprom_read(self.control_board.EEPROM_VGND_ADDRESS)
            schema_entries.append(
                Integer.named('VGND').using(
                    default=settings['VGND'], optional=True,
                    validators=[ValueAtLeast(minimum=0),
                                ValueAtMost(maximum=255),]),
            )
        else:
            settings['SWITCHING_BOARD_I2C_ADDRESS'] = self.control_board \
                .eeprom_read(self.control_board.EEPROM_SWITCHING_BOARD_I2C_ADDRESS)
            schema_entries.append(
                Integer.named('SWITCHING_BOARD_I2C_ADDRESS').using(
                    default=settings['SWITCHING_BOARD_I2C_ADDRESS'], optional=True,
                    validators=[ValueAtLeast(minimum=0),
                                ValueAtMost(maximum=255),]),
            )
            settings['SIGNAL_GENERATOR_BOARD_I2C_ADDRESS'] = self.control_board \
                .eeprom_read(self.control_board.EEPROM_SIGNAL_GENERATOR_BOARD_I2C_ADDRESS)
            schema_entries.append(
                Integer.named('SIGNAL_GENERATOR_BOARD_I2C_ADDRESS').using(
                    default=settings['SIGNAL_GENERATOR_BOARD_I2C_ADDRESS'], optional=True,
                    validators=[ValueAtLeast(minimum=0),
                                ValueAtMost(maximum=255),]),
            )
        for i in range(len(self.control_board.calibration.R_hv)):
            settings['R_hv_%d' % i] = self.control_board.calibration.R_hv[i]
            schema_entries.append(
                Float.named('R_hv_%d' % i).using(
                    default=settings['R_hv_%d' % i], optional=True,
                    validators=[ValueAtLeast(minimum=0),]))
            settings['C_hv_%d' % i] =\
                self.control_board.calibration.C_hv[i]*1e12
            schema_entries.append(
                Float.named('C_hv_%d' % i).using(
                    default=settings['C_hv_%d' % i], optional=True,
                    validators=[ValueAtLeast(minimum=0),]))
        for i in range(len(self.control_board.calibration.R_fb)):
            settings['R_fb_%d' % i] = self.control_board.calibration.R_fb[i]
            schema_entries.append(
                Float.named('R_fb_%d' % i).using(
                    default=settings['R_fb_%d' % i], optional=True,
                    validators=[ValueAtLeast(minimum=0),]))
            settings['C_fb_%d' % i] = \
                self.control_board.calibration.C_fb[i]*1e12
            schema_entries.append(
                Float.named('C_fb_%d' % i).using(
                    default=settings['C_fb_%d' % i], optional=True,
                    validators=[ValueAtLeast(minimum=0),]))

        form = Form.of(*schema_entries)
        dialog = FormViewDialog('Edit calibration settings')
        valid, response =  dialog.run(form)
        if valid:
            for k, v in response.items():
                if settings[k] != v:
                    m = re.match('(R|C)_(hv|fb)_(\d)', k)
                    if k=='amplifier_gain':
                        self.control_board.set_amplifier_gain(v)
                    elif k=='auto_adjust_amplifier_gain':
                        self.control_board.set_auto_adjust_amplifier_gain(v)
                    elif k=='WAVEOUT_GAIN_1':
                        self.control_board.eeprom_write(
                            self.control_board.EEPROM_WAVEOUT_GAIN_1_ADDRESS, v)
                    elif k=='VGND':
                        self.control_board.eeprom_write(
                            self.control_board.EEPROM_VGND_ADDRESS, v)
                    elif k=='SWITCHING_BOARD_I2C_ADDRESS':
                        self.control_board.eeprom_write(
                            self.control_board.EEPROM_SWITCHING_BOARD_I2C_ADDRESS, v)
                    elif k=='SIGNAL_GENERATOR_BOARD_I2C_ADDRESS':
                        self.control_board.eeprom_write(
                            self.control_board.EEPROM_SIGNAL_GENERATOR_BOARD_I2C_ADDRESS, v)
                    elif k=='voltage_tolerance':
                        self.control_board.set_voltage_tolerance(v)
                    elif m:
                        series_resistor = int(m.group(3))
                        if m.group(2)=='hv':
                            channel = 0
                        else:
                            channel = 1
                        self.control_board.set_series_resistor_index(channel,
                            series_resistor)
                        if m.group(1)=='R':
                            self.control_board.set_series_resistance(channel, v)
                        else:
                            if v is None:
                                v=0
                            self.control_board.set_series_capacitance(channel,
                                v/1e12)
            # reconnect to update settings
            self.connect()
            if get_app().protocol:
                self.on_step_run()
Exemplo n.º 10
0
class DmfControlBoardPlugin(Plugin, StepOptionsController, AppDataController):
    """
    This class is automatically registered with the PluginManager.
    """
    implements(IPlugin)
    implements(IWaveformGenerator)

    serial_ports_ = [
        port for port in serial_device.SerialDevice().get_serial_ports()
    ]
    if len(serial_ports_):
        default_port_ = serial_ports_[0]
    else:
        default_port_ = None

    AppFields = Form.of(
        Integer.named('sampling_time_ms').using(default=10, optional=True,
            validators=[ValueAtLeast(minimum=0), ],),
        Integer.named('delay_between_samples_ms').using(default=0,
            optional=True, validators=[ValueAtLeast(minimum=0), ],),
        Enum.named('serial_port').using(default=default_port_, optional=True)\
            .valued(*serial_ports_),
    )

    StepFields = Form.of(
        Integer.named('duration').using(default=100,
                                        optional=True,
                                        validators=[
                                            ValueAtLeast(minimum=0),
                                        ]),
        Float.named('voltage').using(default=100,
                                     optional=True,
                                     validators=[
                                         ValueAtLeast(minimum=0),
                                     ]),
        Float.named('frequency').using(default=1e3,
                                       optional=True,
                                       validators=[
                                           ValueAtLeast(minimum=0),
                                       ]),
        Boolean.named('feedback_enabled').using(default=True, optional=True),
    )
    _feedback_fields = set(['feedback_enabled'])
    version = get_plugin_info(path(__file__).parent.parent).version

    def __init__(self):
        self.control_board = DmfControlBoard()
        self.name = get_plugin_info(path(__file__).parent.parent).plugin_name
        self.url = self.control_board.host_url()
        self.steps = []  # list of steps in the protocol
        self.feedback_options_controller = None
        self.feedback_results_controller = None
        self.feedback_calibration_controller = None
        self.initialized = False
        self.connection_status = "Not connected"
        self.n_voltage_adjustments = None
        self.amplifier_gain_initialized = False
        self.current_frequency = None
        self.edit_log_calibration_menu_item = gtk.MenuItem("Edit calibration")
        self.save_log_calibration_menu_item = \
            gtk.MenuItem("Save calibration to file")
        self.load_log_calibration_menu_item = \
            gtk.MenuItem("Load calibration from file")
        self.timeout_id = None

    def on_plugin_enable(self):
        if not self.initialized:
            self.feedback_options_controller = FeedbackOptionsController(self)
            self.feedback_results_controller = FeedbackResultsController(self)
            self.feedback_calibration_controller = \
                FeedbackCalibrationController(self)
            self.edit_log_calibration_menu_item.connect(
                "activate",
                self.feedback_calibration_controller.on_edit_log_calibration)
            self.save_log_calibration_menu_item.connect(
                "activate",
                self.feedback_calibration_controller.on_save_log_calibration)
            self.load_log_calibration_menu_item.connect(
                "activate",
                self.feedback_calibration_controller.on_load_log_calibration)

            experiment_log_controller = get_service_instance_by_name(
                "microdrop.gui.experiment_log_controller", "microdrop")
            if hasattr(experiment_log_controller, 'popup'):
                experiment_log_controller.popup.add_item(
                    self.edit_log_calibration_menu_item)
                experiment_log_controller.popup.add_item(
                    self.save_log_calibration_menu_item)
                experiment_log_controller.popup.add_item(
                    self.load_log_calibration_menu_item)

            app = get_app()
            self.control_board_menu_item = gtk.MenuItem("DMF control board")
            app.main_window_controller.menu_tools.append(
                self.control_board_menu_item)

            self.control_board_menu = gtk.Menu()
            self.control_board_menu.show()
            self.control_board_menu_item.set_submenu(self.control_board_menu)

            self.feedback_options_controller.on_plugin_enable()

            menu_item = gtk.MenuItem("Perform calibration")
            menu_item.connect(
                "activate",
                self.feedback_calibration_controller.on_perform_calibration)
            self.control_board_menu.append(menu_item)
            self.perform_calibration_menu_item = menu_item
            menu_item.show()

            menu_item = gtk.MenuItem("Load calibration from file")
            menu_item.connect("activate",
                              self.feedback_calibration_controller. \
                                  on_load_calibration_from_file)
            self.control_board_menu.append(menu_item)
            self.load_calibration_from_file_menu_item = menu_item
            menu_item.show()

            menu_item = gtk.MenuItem("Edit calibration settings")
            menu_item.connect("activate", self.on_edit_calibration)
            self.control_board_menu.append(menu_item)
            self.edit_calibration_menu_item = menu_item
            menu_item.show()

            menu_item = gtk.MenuItem("Reset calibration to default values")
            menu_item.connect("activate",
                              self.on_reset_calibration_to_default_values)
            self.control_board_menu.append(menu_item)
            self.reset_calibration_to_default_values_menu_item = menu_item
            menu_item.show()

            self.initialized = True

        super(DmfControlBoardPlugin, self).on_plugin_enable()
        self.check_device_name_and_version()
        self.control_board_menu_item.show()
        self.edit_log_calibration_menu_item.show()
        self.feedback_results_controller.feedback_results_menu_item.show()
        if get_app().protocol:
            self.on_step_run()
            pgc = get_service_instance(ProtocolGridController, env='microdrop')
            pgc.update_grid()

    def on_plugin_disable(self):
        self.feedback_options_controller.on_plugin_disable()
        self.control_board_menu_item.hide()
        self.edit_log_calibration_menu_item.hide()
        self.feedback_results_controller.window.hide()
        self.feedback_results_controller.feedback_results_menu_item.hide()
        if get_app().protocol:
            self.on_step_run()
            pgc = get_service_instance(ProtocolGridController, env='microdrop')
            pgc.update_grid()

    def on_app_options_changed(self, plugin_name):
        if plugin_name == self.name:
            app_values = self.get_app_values()
            if self.control_board.connected() and \
            self.control_board.port != app_values['serial_port']:
                self.connect()

    def connect(self):
        self.current_frequency = None
        self.amplifier_gain_initialized = False
        if len(DmfControlBoardPlugin.serial_ports_):
            app_values = self.get_app_values()
            # try to connect to the last successful port
            try:
                self.control_board.connect(str(app_values['serial_port']))
            except Exception, why:
                logger.warning(
                    'Could not connect to control board on port %s. '
                    'Checking other ports...' % app_values['serial_port'])
                self.control_board.connect()
            app_values['serial_port'] = self.control_board.port
            self.set_app_values(app_values)
        else:
Exemplo n.º 11
0
    def on_edit_calibration(self, widget=None, data=None):
        if not self.control_board.connected():
            logging.error("A control board must be connected in order to "
                          "edit calibration settings.")
            return

        hardware_version = utility.Version.fromstring(
            self.control_board.hardware_version())

        schema_entries = []
        settings = {}
        settings['amplifier_gain'] = self.control_board.amplifier_gain()
        schema_entries.append(
            Float.named('amplifier_gain').using(
                default=settings['amplifier_gain'],
                optional=True,
                validators=[
                    ValueAtLeast(minimum=0.01),
                ]), )
        settings['auto_adjust_amplifier_gain'] = self.control_board \
            .auto_adjust_amplifier_gain()
        schema_entries.append(
            Boolean.named('auto_adjust_amplifier_gain').using(
                default=settings['auto_adjust_amplifier_gain'],
                optional=True), )
        settings['voltage_tolerance'] = \
            self.control_board.voltage_tolerance()
        schema_entries.append(
            Float.named('voltage_tolerance').using(
                default=settings['voltage_tolerance'],
                optional=True,
                validators=[
                    ValueAtLeast(minimum=0),
                ]), )

        if hardware_version.major == 1:
            settings['WAVEOUT_GAIN_1'] = self.control_board \
                .eeprom_read(self.control_board.EEPROM_WAVEOUT_GAIN_1_ADDRESS)
            schema_entries.append(
                Integer.named('WAVEOUT_GAIN_1').using(
                    default=settings['WAVEOUT_GAIN_1'],
                    optional=True,
                    validators=[
                        ValueAtLeast(minimum=0),
                        ValueAtMost(maximum=255),
                    ]), )
            settings['VGND'] = self.control_board \
                .eeprom_read(self.control_board.EEPROM_VGND_ADDRESS)
            schema_entries.append(
                Integer.named('VGND').using(default=settings['VGND'],
                                            optional=True,
                                            validators=[
                                                ValueAtLeast(minimum=0),
                                                ValueAtMost(maximum=255),
                                            ]), )
        else:
            settings['SWITCHING_BOARD_I2C_ADDRESS'] = self.control_board \
                .eeprom_read(self.control_board.EEPROM_SWITCHING_BOARD_I2C_ADDRESS)
            schema_entries.append(
                Integer.named('SWITCHING_BOARD_I2C_ADDRESS').using(
                    default=settings['SWITCHING_BOARD_I2C_ADDRESS'],
                    optional=True,
                    validators=[
                        ValueAtLeast(minimum=0),
                        ValueAtMost(maximum=255),
                    ]), )
            settings['SIGNAL_GENERATOR_BOARD_I2C_ADDRESS'] = self.control_board \
                .eeprom_read(self.control_board.EEPROM_SIGNAL_GENERATOR_BOARD_I2C_ADDRESS)
            schema_entries.append(
                Integer.named('SIGNAL_GENERATOR_BOARD_I2C_ADDRESS').using(
                    default=settings['SIGNAL_GENERATOR_BOARD_I2C_ADDRESS'],
                    optional=True,
                    validators=[
                        ValueAtLeast(minimum=0),
                        ValueAtMost(maximum=255),
                    ]), )
        for i in range(len(self.control_board.calibration.R_hv)):
            settings['R_hv_%d' % i] = self.control_board.calibration.R_hv[i]
            schema_entries.append(
                Float.named('R_hv_%d' % i).using(default=settings['R_hv_%d' %
                                                                  i],
                                                 optional=True,
                                                 validators=[
                                                     ValueAtLeast(minimum=0),
                                                 ]))
            settings['C_hv_%d' % i] =\
                self.control_board.calibration.C_hv[i]*1e12
            schema_entries.append(
                Float.named('C_hv_%d' % i).using(default=settings['C_hv_%d' %
                                                                  i],
                                                 optional=True,
                                                 validators=[
                                                     ValueAtLeast(minimum=0),
                                                 ]))
        for i in range(len(self.control_board.calibration.R_fb)):
            settings['R_fb_%d' % i] = self.control_board.calibration.R_fb[i]
            schema_entries.append(
                Float.named('R_fb_%d' % i).using(default=settings['R_fb_%d' %
                                                                  i],
                                                 optional=True,
                                                 validators=[
                                                     ValueAtLeast(minimum=0),
                                                 ]))
            settings['C_fb_%d' % i] = \
                self.control_board.calibration.C_fb[i]*1e12
            schema_entries.append(
                Float.named('C_fb_%d' % i).using(default=settings['C_fb_%d' %
                                                                  i],
                                                 optional=True,
                                                 validators=[
                                                     ValueAtLeast(minimum=0),
                                                 ]))

        form = Form.of(*schema_entries)
        dialog = FormViewDialog('Edit calibration settings')
        valid, response = dialog.run(form)
        if valid:
            for k, v in response.items():
                if settings[k] != v:
                    m = re.match('(R|C)_(hv|fb)_(\d)', k)
                    if k == 'amplifier_gain':
                        self.control_board.set_amplifier_gain(v)
                    elif k == 'auto_adjust_amplifier_gain':
                        self.control_board.set_auto_adjust_amplifier_gain(v)
                    elif k == 'WAVEOUT_GAIN_1':
                        self.control_board.eeprom_write(
                            self.control_board.EEPROM_WAVEOUT_GAIN_1_ADDRESS,
                            v)
                    elif k == 'VGND':
                        self.control_board.eeprom_write(
                            self.control_board.EEPROM_VGND_ADDRESS, v)
                    elif k == 'SWITCHING_BOARD_I2C_ADDRESS':
                        self.control_board.eeprom_write(
                            self.control_board.
                            EEPROM_SWITCHING_BOARD_I2C_ADDRESS, v)
                    elif k == 'SIGNAL_GENERATOR_BOARD_I2C_ADDRESS':
                        self.control_board.eeprom_write(
                            self.control_board.
                            EEPROM_SIGNAL_GENERATOR_BOARD_I2C_ADDRESS, v)
                    elif k == 'voltage_tolerance':
                        self.control_board.set_voltage_tolerance(v)
                    elif m:
                        series_resistor = int(m.group(3))
                        if m.group(2) == 'hv':
                            channel = 0
                        else:
                            channel = 1
                        self.control_board.set_series_resistor_index(
                            channel, series_resistor)
                        if m.group(1) == 'R':
                            self.control_board.set_series_resistance(
                                channel, v)
                        else:
                            if v is None:
                                v = 0
                            self.control_board.set_series_capacitance(
                                channel, v / 1e12)
            # reconnect to update settings
            self.connect()
            if get_app().protocol:
                self.on_step_run()
Exemplo n.º 12
0
    """
    from flatland import Boolean, Form, String, Integer, Float

    def is_float(v):
        try:
            return (float(str(v)), True)[1]
        except (ValueError, TypeError), e:
            return False

    def is_int(v):
        try:
            return (int(str(v)), True)[1]
        except (ValueError, TypeError), e:
            return False

    def is_bool(v):
        return v in (True, False)

    schema_entries = []
    for k, v in dict.iteritems():
        if is_int(v):
            schema_entries.append(Integer.named(k).using(default=v, optional=True))
        elif is_float(v):
            schema_entries.append(Float.named(k).using(default=v, optional=True))
        elif is_bool(v):
            schema_entries.append(Boolean.named(k).using(default=v, optional=True))
        elif type(v) == str:
            schema_entries.append(String.named(k).using(default=v, optional=True))

    return Form.of(*schema_entries)
class ZeroMQServicePlugin(Plugin, AppDataController, StepOptionsController):
    """
    This class is automatically registered with the PluginManager.
    """
    implements(IPlugin)
    version = get_plugin_info(path(__file__).parent.parent).version
    plugins_name = get_plugin_info(path(__file__).parent.parent).plugin_name
    '''
    AppFields
    ---------

    A flatland Form specifying application options for the current plugin.
    Note that nested Form objects are not supported.

    Since we subclassed AppDataController, an API is available to access and
    modify these attributes.  This API also provides some nice features
    automatically:
        -all fields listed here will be included in the app options dialog
            (unless properties=dict(show_in_gui=False) is used)
        -the values of these fields will be stored persistently in the microdrop
            config file, in a section named after this plugin's name attribute
    '''
    AppFields = Form.of(
        String.named('service_address').using(default='', optional=True), )
    '''
    StepFields
    ---------

    A flatland Form specifying the per step options for the current plugin.
    Note that nested Form objects are not supported.

    Since we subclassed StepOptionsController, an API is available to access and
    modify these attributes.  This API also provides some nice features
    automatically:
        -all fields listed here will be included in the protocol grid view
            (unless properties=dict(show_in_gui=False) is used)
        -the values of these fields will be stored persistently for each step
    '''
    StepFields = Form.of(
        Boolean.named('service_enabled').using(default=False, optional=True),
        Float.named('timeout_sec').using(default=5., optional=True),
    )

    def __init__(self):
        self.name = self.plugins_name
        self.context = zmq.Context.instance()
        self.socks = OrderedDict()
        self.timeout_id = None
        self._start_time = None

    def on_plugin_enable(self):
        # We need to call AppDataController's on_plugin_enable() to update the
        # application options data.
        AppDataController.on_plugin_enable(self)
        self.context = zmq.Context()
        self.reset_socks()
        if get_app().protocol:
            pgc = get_service_instance(ProtocolGridController, env='microdrop')
            pgc.update_grid()

    def close_socks(self):
        # Close any currently open sockets.
        for name, sock in self.socks.iteritems():
            sock.close()
        self.socks = OrderedDict()

    def reset_socks(self):
        self.close_socks()
        app_values = self.get_app_values()
        if self.timeout_id is not None:
            gtk.timeout_remove(self.timeout_id)
            self.timeout_id = None
        if app_values['service_address']:
            # Service address is available
            self.socks['req'] = zmq.Socket(self.context, zmq.REQ)
            self.socks['req'].connect(app_values['service_address'])

    def on_app_options_changed(self, plugin_name):
        if plugin_name == self.name:
            self.reset_socks()

    def on_plugin_disable(self):
        self.close_socks()
        if get_app().protocol:
            pgc = get_service_instance(ProtocolGridController, env='microdrop')
            pgc.update_grid()

    def _on_check_service_response(self, timeout_sec):
        if not self.socks['req'].poll(timeout=11):
            # No response is ready yet.
            if timeout_sec < (datetime.now() -
                              self._start_time).total_seconds():
                # Timed out waiting for response.
                self.reset_socks()
                self.step_complete(return_value='Fail')
                self.timeout_id = None
                return False
            return True
        else:
            # Response is ready.
            response = self.socks['req'].recv()
            logger.info('[ZeroMQServicePlugin] Service response: %s', response)
            if response == 'completed':
                logger.info('[ZeroMQServicePlugin] Service completed task '
                            'successfully.')
                self.step_complete()
            else:
                logger.error('[ZeroMQServicePlugin] Unexpected response: %s' %
                             response)
                self.step_complete(return_value='Fail')
            self.timeout_id = None
            return False

    def step_complete(self, return_value=None):
        app = get_app()
        if app.running or app.realtime_mode:
            emit_signal('on_step_complete', [self.name, return_value])

    def on_step_run(self):
        options = self.get_step_options()
        self.reset_socks()
        if options['service_enabled'] and self.socks['req'] is None:
            # Service is supposed to be called for this step, but the socket is
            # not ready.
            self.step_complete(return_value='Fail')
        elif options['service_enabled'] and self.socks['req'] is not None:
            logger.info('[ZeroMQServicePlugin] Send signal to service to '
                        'start.')
            # Request start of service.
            self.socks['req'].send('start')
            if not self.socks['req'].poll(timeout=4000):
                self.reset_socks()
                logger.error('[ZeroMQServicePlugin] Timed-out waiting for '
                             'a response.')
            else:
                # Response is ready.
                response = self.socks['req'].recv()
                if response == 'started':
                    logger.info('[ZeroMQServicePlugin] Service started '
                                'successfully.')
                    self.socks['req'].send('notify_completion')
                    self._start_time = datetime.now()
                    self.timeout_id = gtk.timeout_add(
                        100, self._on_check_service_response,
                        options['timeout_sec'])
        else:
            self.step_complete()

    def enable_service(self):
        pass
Exemplo n.º 14
0
class SyringePumpPlugin(Plugin, AppDataController, StepOptionsController):
    """
    This class is automatically registered with the PluginManager.
    """
    implements(IPlugin)
    version = get_plugin_info(path(__file__).parent).version
    plugin_name = get_plugin_info(path(__file__).parent).plugin_name

    serial_ports_ = [port for port in get_serial_ports()]
    if len(serial_ports_):
        default_port_ = serial_ports_[0]
    else:
        default_port_ = None
    '''
    AppFields
    ---------

    A flatland Form specifying application options for the current plugin.
    Note that nested Form objects are not supported.

    Since we subclassed AppDataController, an API is available to access and
    modify these attributes.  This API also provides some nice features
    automatically:
        -all fields listed here will be included in the app options dialog
            (unless properties=dict(show_in_gui=False) is used)
        -the values of these fields will be stored persistently in the microdrop
            config file, in a section named after this plugin's name attribute
    '''
    AppFields = Form.of(
        Enum.named('serial_port').using(default=default_port_,
                                        optional=True).valued(*serial_ports_),
        Float.named('steps_per_microliter').using(optional=True, default=1.0),
    )
    '''
    StepFields
    ---------

    A flatland Form specifying the per step options for the current plugin.
    Note that nested Form objects are not supported.

    Since we subclassed StepOptionsController, an API is available to access and
    modify these attributes.  This API also provides some nice features
    automatically:
        -all fields listed here will be included in the protocol grid view
            (unless properties=dict(show_in_gui=False) is used)
        -the values of these fields will be stored persistently for each step
    '''
    StepFields = Form.of(
        Float.named('microliters_per_min').using(
            optional=True,
            default=60,
            #validators=
            #[ValueAtLeast(minimum=0),
            # ValueAtMost(maximum=100000)]
        ),
        Float.named('microliters').using(
            optional=True,
            default=10,
            #validators=
            #[ValueAtLeast(minimum=0),
            # ValueAtMost(maximum=100000)]
        ),
    )

    def __init__(self):
        self.name = self.plugin_name
        self.proxy = None
        self.initialized = False  # Latch to, e.g., config menus, only once

    def connect(self):
        """ 
        Try to connect to the syring pump at the default serial
        port selected in the Microdrop application options.

        If unsuccessful, try to connect to the proxy on any
        available serial port, one-by-one.
        """

        from stepper_motor_controller import SerialProxy

        if len(SyringePumpPlugin.serial_ports_):
            app_values = self.get_app_values()
            # try to connect to the last successful port
            try:
                self.proxy = SerialProxy(port=str(app_values['serial_port']))
            except:
                logger.warning(
                    'Could not connect to the syringe pump on port %s. '
                    'Checking other ports...',
                    app_values['serial_port'],
                    exc_info=True)
                self.proxy = SerialProxy()
            app_values['serial_port'] = self.proxy.port
            self.set_app_values(app_values)
        else:
            raise Exception("No serial ports available.")

    def check_device_name_and_version(self):
        """
        Check to see if:

         a) The connected device is a what we are expecting
         b) The device firmware matches the host driver API version

        In the case where the device firmware version does not match, display a
        dialog offering to flash the device with the firmware version that
        matches the host driver API version.
        """
        try:
            self.connect()
            properties = self.proxy.properties
            package_name = properties['package_name']
            display_name = properties['display_name']
            if package_name != self.proxy.host_package_name:
                raise Exception("Device is not a %s" %
                                properties['display_name'])

            host_software_version = self.proxy.host_software_version
            remote_software_version = self.proxy.remote_software_version

            # Reflash the firmware if it is not the right version.
            if host_software_version != remote_software_version:
                response = yesno("The %s firmware version (%s) "
                                 "does not match the driver version (%s). "
                                 "Update firmware?" %
                                 (display_name, remote_software_version,
                                  host_software_version))
                if response == gtk.RESPONSE_YES:
                    self.on_flash_firmware()
        except Exception, why:
            logger.warning("%s" % why)