class OpenDropPlugin(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_),
        Integer.named('baud_rate')
        .using(default=115200, optional=True, validators=[ValueAtLeast(minimum=0),
                                                     ],),
    )

    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),
                                                 max_voltage]),
        Float.named('frequency').using(default=10e3, optional=True,
                                       validators=[ValueAtLeast(minimum=0),
                                                   check_frequency]),
    )

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

    def __init__(self):
        self.control_board = OpenDropBoard()
        self.name = get_plugin_info(path(__file__).parent).plugin_name
        self.connection_status = "Not connected"
        self.current_frequency = None
        self.timeout_id = None

    def on_plugin_enable(self):
        super(OpenDropPlugin, self).on_plugin_enable()
        self.check_device_name_and_version()
        if get_app().protocol:
            self.on_step_run()
            self._update_protocol_grid()

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

    def on_protocol_swapped(self, old_protocol, protocol):
        self._update_protocol_grid()
        
    def _update_protocol_grid(self):
        app = get_app()
        app_values = self.get_app_values()
        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.connected():
                for k, v in app_values.items():
                    if k == 'baud_rate' and self.control_board.baud_rate != v:
                        self.control_board.baud_rate = v
                        reconnect = True
                    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.connected() and not app.realtime_mode and
                not app.running):
                logger.info('Turning off all electrodes.')
                self.control_board.set_state_of_all_channels(
                    np.zeros(self.control_board.number_of_channels())
                )

    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(OpenDropPlugin.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']),
                    app_values['baud_rate'])
            except RuntimeError, why:
                logger.warning('Could not connect to control board on port %s.'
                               ' Checking other ports... [%s]' %
                               (app_values['serial_port'], why))
                
                self.control_board.connect(baud_rate=app_values['baud_rate'])
            app_values['serial_port'] = self.control_board.port
            self.set_app_values(app_values)
        else:
 def __init__(self):
     self.control_board = OpenDropBoard()
     self.name = get_plugin_info(path(__file__).parent).plugin_name
     self.connection_status = "Not connected"
     self.current_frequency = None
     self.timeout_id = None