Exemplo n.º 1
0
    def _populate_app_fields(self):
        with WindowServiceProxy(59000) as w:
            self.video_mode_map = w.get_video_mode_map()
            if self.video_mode_map:
                self._video_available = True
            else:
                self._video_available = False
            self.video_mode_keys = sorted(self.video_mode_map.keys())
            if self._video_available:
                self.device_key, self.devices = w.get_video_source_configs()

        field_list = [
            Integer.named('overlay_opacity').using(default=50, optional=True),
            Directory.named('device_directory').using(default='', optional=True),
            String.named('transform_matrix').using(default='', optional=True,
                                                properties={'show_in_gui':
                                                            False}), ]

        if self._video_available:
            video_mode_enum = Enum.named('video_mode').valued(
                *self.video_mode_keys).using(default=self.video_mode_keys[0],
                                             optional=True)
            video_enabled_boolean = Boolean.named('video_enabled').using(
                default=False, optional=True, properties={'show_in_gui': True})
            recording_enabled_boolean = Boolean.named('recording_enabled').using(
                default=False, optional=True, properties={'show_in_gui': False})
            field_list.append(video_mode_enum)
            field_list.append(video_enabled_boolean)
            field_list.append(recording_enabled_boolean)
        return Form.of(*field_list)
Exemplo n.º 2
0
class PersonForm(FormView):

    schema_type = Dict.of(
        String.named('name'),
        Integer.named('age'),
        Boolean.named('friendly'),
    )
Exemplo n.º 3
0
def small_form(values=None):

    SmallForm = Dict.of(String.named(u'field1'), String.named(u'field2'),
                        Boolean.named(u'toggle1'), Boolean.named(u'toggle2'),
                        Array.named(u'multi').of(String),
                        DateYYYYMMDD.named(u'date1'))

    if values is None:
        values = {
            u'field1': u'val',
            u'toggle2': True,
            u'multi': [u'a', u'b'],
            u'date1': datetime.date(1999, 12, 31),
        }
    el = SmallForm(values)
    return {'form': el}
Exemplo n.º 4
0
class PersonSchema(Form):

    name = String

    age = Integer.using(validators=[
        ValueAtLeast(minimum=18),
        ValueAtMost(maximum=120)
    ])

    weight = Integer.using(validators=[
        ValueAtLeast(minimum=0),
        ValueAtMost(maximum=300)
    ])
    weight.render_options = dict(
        style='slider'
    )

    friendly = Boolean

    address = String.using()
    address.render_options = dict(
        style='multiline'
    )

    happy = Boolean.using()
    happy.render_options = dict(
        style='toggle'
    )
Exemplo n.º 5
0
def small_form(values=None):

    SmallForm = Dict.of(
        String.named(u'field1'),
        String.named(u'field2'),
        Boolean.named(u'toggle1'),
        Boolean.named(u'toggle2'),
        Array.named(u'multi').of(String),
        DateYYYYMMDD.named(u'date1'))

    if values is None:
        values = {
            u'field1': u'val',
            u'toggle2': True,
            u'multi': [u'a', u'b'],
            u'date1': datetime.date(1999, 12, 31),
            }
    el = SmallForm(values)
    return {'form': el}
Exemplo n.º 6
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.º 7
0
    def __init__(self):
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_title("Mpeg2-Player")
        window.set_default_size(640, 500)
        window.connect("destroy", self.on_destroy)
        vbox = gtk.VBox()
        window.add(vbox)
        hbox = gtk.HBox()
        vbox.pack_start(hbox, expand=False)

        video_mode_enum = Enum.named('video_mode').valued(*self.video_mode_keys)
        form = Form.of(
            video_mode_enum.using(default=self.video_mode_keys[0]),
            Filepath.named('output_path').using(default=''),
            Integer.named('bitrate').using(default=150, validators=[ValueAtLeast(
                    minimum=25)], properties={'step': 25,
                            'label': 'Bitrate (KB/s)', }),
            String.named('transform_string').using(default='1,0,0,0,1,0,0,0,1'),
            Boolean.named('draw_cairo').using(default=False),
        )
        self.video_mode_form_view = create_form_view(form)
        for field in ['video_mode', 'output_path', 'bitrate',
                'transform_string', 'draw_cairo']:
            setattr(self, '%s_field' % field, self.video_mode_form_view.form\
                    .fields[field])
        self.video_mode_field.proxy.connect('changed', self._on_mode_changed)
        self.video_source = None
        hbox.add(self.video_mode_form_view.widget)
        self.button = gtk.Button("Start")
        hbox.pack_start(self.button, False)
        self.button.connect("clicked", self.start_stop)
        self.aframe = gtk.AspectFrame(xalign=0.5, yalign=1.0, ratio=4.0 / 3.0,
                obey_child=False)

        self.pipeline = None
        self._proxy = None

        vbox.pack_start(self.aframe, expand=True)
        self.movie_view = GtkVideoView()
        self.movie_window = self.movie_view.widget
        self.aframe.add(self.movie_window)
        window.show_all()
        self.window = window
Exemplo n.º 8
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.º 9
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.º 10
0
    lang='en', dir='ltr').validated_by(ValidJSON())

URL = String.with_properties(widget=WIDGET_TEXT).validated_by(URLValidator())

Email = String.using(label=L_('E-Mail')).with_properties(
    widget=WIDGET_EMAIL,
    placeholder=L_("E-Mail address")).validated_by(IsEmail())

YourEmail = Email.with_properties(placeholder=L_("Your E-Mail address"))

Password = Text.with_properties(widget=WIDGET_PASSWORD).using(
    label=L_('Password'))

RequiredPassword = Password.validated_by(Present())

Checkbox = Boolean.with_properties(widget=WIDGET_CHECKBOX).using(optional=True,
                                                                 default=1)

InlineCheckbox = Checkbox.with_properties(widget=WIDGET_INLINE_CHECKBOX)

Select = Enum.with_properties(widget=WIDGET_SELECT)

# SelectSubmit is like Select in that it is rendered as a group of controls
# with different (predefined) `value`s for the same `name`. But the controls are
# submit buttons instead of radio buttons.
#
# This is used to present the user several "OK" buttons with slightly different
# semantics, like "Update" and "Update and Close" on a ticket page, or
# "Save as Draft" and "Publish" when editing a blog entry.
SelectSubmit = Enum.with_properties(widget=WIDGET_SELECT_SUBMIT)

Exemplo n.º 11
0
URL = String.with_properties(widget=WIDGET_TEXT).validated_by(URLValidator())

OpenID = URL.using(label=L_('OpenID')).with_properties(placeholder=L_("OpenID address"))

YourOpenID = OpenID.with_properties(placeholder=L_("Your OpenID address"))

Email = String.using(label=L_('E-Mail')).with_properties(widget=WIDGET_EMAIL,
                                                         placeholder=L_("E-Mail address")).validated_by(IsEmail())

YourEmail = Email.with_properties(placeholder=L_("Your E-Mail address"))

Password = Text.with_properties(widget=WIDGET_PASSWORD).using(label=L_('Password'))

RequiredPassword = Password.validated_by(Present())

Checkbox = Boolean.with_properties(widget=WIDGET_CHECKBOX).using(optional=True, default=1)

InlineCheckbox = Checkbox.with_properties(widget=WIDGET_INLINE_CHECKBOX)

Select = Enum.with_properties(widget=WIDGET_SELECT)

# SelectSubmit is like Select in that it is rendered as a group of controls
# with different (predefined) `value`s for the same `name`. But the controls are
# submit buttons instead of radio buttons.
#
# This is used to present the user several "OK" buttons with slightly different
# semantics, like "Update" and "Update and Close" on a ticket page, or
# "Save as Draft" and "Publish" when editing a blog entry.
SelectSubmit = Enum.with_properties(widget=WIDGET_SELECT_SUBMIT)

Exemplo n.º 12
0
class App(SingletonPlugin, AppDataController):
    implements(IPlugin)
    '''
INFO:  <Plugin App 'microdrop.app'>
INFO:  <Plugin ConfigController 'microdrop.gui.config_controller'>
INFO:  <Plugin DmfDeviceController 'microdrop.gui.dmf_device_controller'>
INFO:  <Plugin ExperimentLogController
         'microdrop.gui.experiment_log_controller'>
INFO:  <Plugin MainWindowController 'microdrop.gui.main_window_controller'>
INFO:  <Plugin ProtocolController 'microdrop.gui.protocol_controller'>
INFO:  <Plugin ProtocolGridController 'microdrop.gui.protocol_grid_controller'>
    '''
    core_plugins = [
        'microdrop.app', 'microdrop.gui.config_controller',
        'microdrop.gui.dmf_device_controller',
        'microdrop.gui.experiment_log_controller',
        'microdrop.gui.main_window_controller',
        'microdrop.gui.protocol_controller',
        'microdrop.gui.protocol_grid_controller', 'microdrop.zmq_hub_plugin',
        'microdrop.electrode_controller_plugin', 'microdrop.device_info_plugin'
    ]

    AppFields = Form.of(
        Integer.named('x').using(default=None,
                                 optional=True,
                                 properties={'show_in_gui': False}),
        Integer.named('y').using(default=None,
                                 optional=True,
                                 properties={'show_in_gui': False}),
        Integer.named('width').using(default=400,
                                     optional=True,
                                     properties={'show_in_gui': False}),
        Integer.named('height').using(default=500,
                                      optional=True,
                                      properties={'show_in_gui': False}),
        String.named('server_url').using(
            default='http://microfluidics.utoronto.ca/update',
            optional=True,
            properties=dict(show_in_gui=False)),
        Boolean.named('realtime_mode').using(
            default=False, optional=True, properties=dict(show_in_gui=False)),
        Filepath.named('log_file').using(
            default='',
            optional=True,
            properties={'action': gtk.FILE_CHOOSER_ACTION_SAVE}),
        Boolean.named('log_enabled').using(default=False, optional=True),
        Enum.named('log_level').using(default='info', optional=True).valued(
            'debug', 'info', 'warning', 'error', 'critical'))

    def __init__(self):
        '''
        .. versionchanged:: 2.11.2
            Add :attr:`gtk_thread` attribute, holding a reference to the thread
            that the GTK main loop is executing in.

        .. versionchanged:: 2.17
            Remove :attr:`version` attribute.  Use
            :attr:`microdrop.__version__` instead.
        '''
        args = parse_args()

        print 'Arguments: %s' % args

        self.name = "microdrop.app"
        #: .. versionadded:: 2.11.2
        self.gtk_thread = None

        self.realtime_mode = False
        self.running = False
        self.builder = gtk.Builder()
        self.signals = {}
        self.plugin_data = {}

        # these members are initialized by plugins
        self.experiment_log_controller = None
        self.config_controller = None
        self.dmf_device_controller = None
        self.protocol_controller = None
        self.main_window_controller = None

        # Enable custom logging handler
        logging.getLogger().addHandler(CustomHandler())
        self.log_file_handler = None

        # config model
        try:
            self.config = Config(args.config)
        except IOError:
            logging.error(
                'Could not read configuration file, `%s`.  Make sure'
                ' it exists and is readable.', args.config)
            raise SystemExit(-1)

        # set the log level
        if self.name in self.config.data and ('log_level'
                                              in self.config.data[self.name]):
            self._set_log_level(self.config.data[self.name]['log_level'])
        _L().info('MicroDrop version: %s', __version__)
        _L().info('Running in working directory: %s', os.getcwd())

        # dmf device
        self.dmf_device = None

        # protocol
        self.protocol = None

    def get_data(self, plugin_name):
        data = self.plugin_data.get(plugin_name)
        if data:
            return data
        else:
            return {}

    def set_data(self, plugin_name, data):
        '''
        .. versionchanged:: 2.20
            Log data and plugin name to debug level.
        '''
        logger = _L()  # use logger with method context
        if logger.getEffectiveLevel() >= logging.DEBUG:
            caller = caller_name(skip=2)
            logger.debug('%s -> plugin_data:', caller)
            map(logger.debug, pprint.pformat(data).splitlines())
        self.plugin_data[plugin_name] = data

    def on_app_options_changed(self, plugin_name):
        if plugin_name == self.name:
            data = self.get_data(self.name)
            if 'realtime_mode' in data:
                if self.realtime_mode != data['realtime_mode']:
                    self.realtime_mode = data['realtime_mode']
                    if self.protocol_controller:
                        self.protocol_controller.run_step()
            if 'log_file' in data and 'log_enabled' in data:
                self.apply_log_file_config(data['log_file'],
                                           data['log_enabled'])
            if 'log_level' in data:
                self._set_log_level(data['log_level'])
            if 'width' in data and 'height' in data:
                self.main_window_controller.view.resize(
                    data['width'], data['height'])
                # allow window to resize before other signals are processed
                while gtk.events_pending():
                    gtk.main_iteration()
            if data.get('x') is not None and data.get('y') is not None:
                self.main_window_controller.view.move(data['x'], data['y'])
                # allow window to resize before other signals are processed
                while gtk.events_pending():
                    gtk.main_iteration()

    def apply_log_file_config(self, log_file, enabled):
        if enabled and not log_file:
            _L().error('Log file can only be enabled if a path is selected.')
            return False
        self.update_log_file()
        return True

    @property
    def plugins(self):
        return set(self.plugin_data.keys())

    def plugin_name_lookup(self, name, re_pattern=False):
        if not re_pattern:
            return name

        for plugin_name in self.plugins:
            if re.search(name, plugin_name):
                return plugin_name
        return None

    def update_plugins(self):
        '''
        .. versionchanged:: 2.16.2
            Method was deprecated.
        '''
        raise DeprecationWarning('The `update_plugins` method was deprecated '
                                 'in version 2.16.2.')

    def gtk_thread_active(self):
        '''
        Returns
        -------
        bool
            ``True`` if the currently active thread is the GTK thread.

        .. versionadded:: 2.11.2
        '''
        if self.gtk_thread is not None and (threading.current_thread().ident
                                            == self.gtk_thread.ident):
            return True
        else:
            return False

    def run(self):
        '''
        .. versionchanged:: 2.11.2
            Set :attr:`gtk_thread` attribute, holding a reference to the thread
            that the GTK main loop is executing in.

        .. versionchanged:: 2.16.2
            Do not attempt to update plugins.
        '''
        logger = _L()  # use logger with method context
        self.gtk_thread = threading.current_thread()

        # set realtime mode to false on startup
        if self.name in self.config.data and \
                'realtime_mode' in self.config.data[self.name]:
            self.config.data[self.name]['realtime_mode'] = False

        plugin_manager.emit_signal('on_plugin_enable')
        log_file = self.get_app_values()['log_file']
        if not log_file:
            self.set_app_values({
                'log_file':
                ph.path(self.config['data_dir']).joinpath('microdrop.log')
            })

        pwd = ph.path(os.getcwd()).realpath()
        if '' in sys.path and pwd.joinpath('plugins').isdir():
            logger.info(
                '[warning] Removing working directory `%s` from Python'
                ' import path.', pwd)
            sys.path.remove('')

        # Import enabled plugins from Conda environment.
        conda_plugins_dir = mpm.api.MICRODROP_CONDA_ETC.joinpath(
            'plugins', 'enabled')
        if conda_plugins_dir.isdir():
            plugin_manager.load_plugins(conda_plugins_dir,
                                        import_from_parent=False)
        self.update_log_file()

        logger.info('User data directory: %s', self.config['data_dir'])
        logger.info('Plugins directory: %s', conda_plugins_dir)
        logger.info('Devices directory: %s', self.get_device_directory())

        FormViewDialog.default_parent = self.main_window_controller.view
        self.builder.connect_signals(self.signals)

        observers = {}
        plugins_to_disable_by_default = []
        # Enable plugins according to schedule requests
        for package_name in self.config['plugins']['enabled']:
            try:
                service = plugin_manager. \
                    get_service_instance_by_package_name(package_name)
                observers[service.name] = service
            except KeyError:
                logger.warning('No plugin found registered with name `%s`',
                               package_name)
                # Mark plugin to be removed from "enabled" list to prevent
                # trying to enable it on future launches.
                plugins_to_disable_by_default.append(package_name)
            except Exception, exception:
                logger.error(exception, exc_info=True)
        # Remove marked plugins from "enabled" list to prevent trying to enable
        # it on future launches.
        for package_name_i in plugins_to_disable_by_default:
            self.config['plugins']['enabled'].remove(package_name_i)

        schedule = plugin_manager.get_schedule(observers, "on_plugin_enable")

        # Load optional plugins marked as enabled in config
        for p in schedule:
            try:
                plugin_manager.enable(p)
            except KeyError:
                logger.warning('Requested plugin (%s) is not available.\n\n'
                               'Please check that it exists in the plugins '
                               'directory:\n\n    %s' %
                               (p, self.config['plugins']['directory']),
                               exc_info=True)
        plugin_manager.log_summary()

        self.experiment_log = None

        # save the protocol name from the config file because it is
        # automatically overwritten when we load a new device
        protocol_name = self.config['protocol']['name']

        # if there is no device specified in the config file, try choosing one
        # from the device directory by default
        device_directory = ph.path(self.get_device_directory())
        if not self.config['dmf_device']['name']:
            try:
                self.config['dmf_device']['name'] = \
                    device_directory.dirs()[0].name
            except Exception:
                pass

        # load the device from the config file
        if self.config['dmf_device']['name']:
            if device_directory:
                device_path = os.path.join(device_directory,
                                           self.config['dmf_device']['name'],
                                           DEVICE_FILENAME)
                self.dmf_device_controller.load_device(device_path)

        # if we successfully loaded a device
        if self.dmf_device:
            # reapply the protocol name to the config file
            self.config['protocol']['name'] = protocol_name

            # load the protocol
            if self.config['protocol']['name']:
                directory = self.get_device_directory()
                if directory:
                    filename = os.path.join(directory,
                                            self.config['dmf_device']['name'],
                                            "protocols",
                                            self.config['protocol']['name'])
                    self.protocol_controller.load_protocol(filename)

        data = self.get_data("microdrop.app")
        x = data.get('x', None)
        y = data.get('y', None)
        width = data.get('width', 400)
        height = data.get('height', 600)
        self.main_window_controller.view.resize(width, height)
        if x is not None and y is not None:
            self.main_window_controller.view.move(x, y)
        plugin_manager.emit_signal('on_gui_ready')
        self.main_window_controller.main()
Exemplo n.º 13
0
    *common_meta
)

UserMetaSchema = DuckDict.named('UserMetaSchema').of(
    String.named(keys.CONTENTTYPE).validated_by(user_contenttype_validator),
    String.named(keys.EMAIL).using(optional=True),
    String.named(keys.OPENID).using(optional=True),
    String.named(keys.ENC_PASSWORD).using(optional=True),
    String.named(keys.RECOVERPASS_KEY).using(optional=True),
    String.named(keys.THEME_NAME).using(optional=True),
    String.named(keys.TIMEZONE).using(optional=True),
    String.named(keys.LOCALE).using(optional=True),
    String.named(keys.CSS_URL).using(optional=True),
    Integer.named(keys.RESULTS_PER_PAGE).using(optional=True),
    Integer.named(keys.EDIT_ROWS).using(optional=True),
    Boolean.named(keys.DISABLED).using(optional=True),
    Boolean.named(keys.WANT_TRIVIAL).using(optional=True),
    Boolean.named(keys.SHOW_COMMENTS).using(optional=True),
    Boolean.named(keys.EDIT_ON_DOUBLECLICK).using(optional=True),
    Boolean.named(keys.SCROLL_PAGE_AFTER_EDIT).using(optional=True),
    Boolean.named(keys.MAILTO_AUTHOR).using(optional=True),
    List.named(keys.QUICKLINKS).of(String.named('quicklinks')).using(optional=True),
    List.named(keys.SUBSCRIPTIONS).of(String.named('subscription').validated_by(subscription_validator)).using(optional=True),
    List.named(keys.EMAIL_SUBSCRIBED_EVENTS).of(String.named('email_subscribed_event')).using(optional=True),
    # TODO: DuckDict.named('bookmarks').using(optional=True),
    *common_meta
)


def validate_data(meta, data):
    """
Exemplo n.º 14
0
#! -*- coding: utf-8 -*-
from flatland import Array, Boolean, Integer
from flatland.out import generic
from flatland.out.generic import Context

from tests._util import unicode_coercion_allowed, textstr

Unspecified = object()
Unique = object()
schema = Integer.named(u'number')
boolean_schema = Boolean.named(u'bool')
partial_anon_schema = Array.named(u'array').of(Integer)
full_anon_schema = Array.of(Integer)


def assert_bound_transform(fn, tagname, given, expected, **kw):
    return assert_transform(fn, tagname, given, expected, **kw)


def assert_unbound_transform(fn, tagname, given, expected, **kw):
    kw['bind'] = None
    return assert_transform(fn, tagname, given, expected, **kw)


def assert_transform(fn,
                     tagname,
                     given,
                     expected,
                     context=Unspecified,
                     bind=Unspecified,
                     contents=Unspecified,
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.º 16
0
class MrBoxPeripheralBoardPlugin(AppDataController, StepOptionsController,
                                 Plugin):
    '''
    This class is automatically registered with the PluginManager.
    '''
    implements(IPlugin)

    plugin_name = str(ph.path(__file__).realpath().parent.name)
    try:
        version = __version__
    except NameError:
        version = 'v0.0.0+unknown'

    AppFields = Form.of(Boolean.named('Use PMT y-axis SI units')
                        .using(default=True, optional=True))

    StepFields = Form.of(# PMT Fields
                         Boolean.named('Measure_PMT')
                         .using(default=False, optional=True),
                         # Only allow PMT Duration to be set if `Measure_PMT`
                         # field is set to `True`.
                         Integer.named('Measurement_duration_(s)')
                         .using(default=10, optional=True,
                                validators=[ValueAtLeast(minimum=0)],
                                properties={'mappers':
                                            [PropertyMapper
                                             ('sensitive', attr='Measure_PMT'),
                                             PropertyMapper
                                             ('editable',
                                              attr='Measure_PMT')]}))
                         # Only allow ADC Gain to be set if `Measure_PMT` field
                         # is set to `True`.
                         # TODO Convert ADC Gain to dropdown list with
                         # valid_values = (1,2,4,8,16)
                        #  Integer.named('ADC_Gain')
                        #  .using(default=1, optional=True,
                        #         validators=[ValueAtLeast(minimum=1),
                        #                     ValueAtMost(maximum=16)],
                        #         properties={'mappers':
                        #                     [PropertyMapper
                        #                      ('sensitive', attr='Measure_PMT'),
                        #                      PropertyMapper
                        #                      ('editable',
                        #                       attr='Measure_PMT')]}),


    def __init__(self):
        super(MrBoxPeripheralBoardPlugin, self).__init__()
        self.board = None
        # XXX `name` attribute is required in addition to `plugin_name`
        #
        # The `name` attribute is required in addition to the `plugin_name`
        # attribute because MicroDrop uses it for plugin labels in, for
        # example, the plugin manager dialog.
        self.name = self.plugin_name

        # Flag to indicate whether user has already been warned about the board
        # not being connected when trying to set board state.
        self._user_warned = False

        # `dropbot.SerialProxy` instance
        self.dropbot_remote = None

        # Latch to, e.g., config menus, only once
        self.initialized = False

        self.adc_gain_calibration = None
        self.adc_offset_calibration = None
        self.off_cal_val = None

    def reset_board_state(self):
        '''
        Reset MR-Box peripheral board to default state.
        '''
        # Reset user warned state (i.e., warn user next time board settings
        # are applied when board is not connected).
        self._user_warned = False

        if self.board is None:
            return

        # TODO Add reset method for each component (PMT)
        # TODO to respective `mr-box-peripheral-board.py` C++ classes code.






        # Set PMT control voltage to zero.
        self.board.pmt_set_pot(0)
        # Start the ADC and Perform ADC Calibration
        MAX11210_begin(self.board)



    def apply_step_options(self, step_options):
        '''
        Apply the specified step options.

        Parameters
        ----------
        step_options : dict
            Dictionary containing the MR-Box peripheral board plugin options
            for a protocol step.
        '''

        app = get_app()
        app_values = self.get_app_values()

        if self.board:
            step_log = {}

            services_by_name = {service_i.name: service_i
                                for service_i in
                                PluginGlobals
                                .env('microdrop.managed').services}

            step_label = None
            if 'wheelerlab.step_label_plugin' in services_by_name:
                # Step label is set for current step
                step_label_plugin = (services_by_name
                                     .get('wheelerlab.step_label_plugin'))
                step_label = (step_label_plugin.get_step_options()
                              or {}).get('label')

            # Apply board hardware options.
            try:

                # PMT/ADC
                # -------
                if step_options.get('Measure_PMT'):

                    # Start the ADC and Perform ADC Calibration
                    MAX11210_begin(self.board)

                    if step_label.lower() == 'background':
                        ''' Set PMT control voltage via digipot.'''
                        # Divide the control voltage by the maximum 1100 mV and
                        # convert it to digipot steps
                        '''
                        Perform certain calibration steps only for the background
                        measurement.

                        Read from the 24bit Registries (SCGC, SCOC)
                        and store their values for the rest of the
                        measurements.
                        '''

                        logger.warning('Open PMT shutter and close box lid')
                        self.adc_gain_calibration = self.board.MAX11210_getSelfCalGain()
                        self.adc_offset_calibration = self.board.MAX11210_getSelfCalOffset()
                        self.board.MAX11210_setSysOffsetCal(0x00)
                        self.board.MAX11210_send_command(0b10001000)
                        reading_i = []
                        for i in range(0,20):
                            self.board.MAX11210_setRate(120)
                            reading_i.append(self.board.MAX11210_getData())
                        reading_avg = (sum(reading_i)* 1.0) / (len(reading_i) * 1.0)
                        self.off_cal_val = int(reading_avg) - 1677

                    else:
                        if not self.adc_gain_calibration:
                            logger.warning('Missing ADC Calibration Values!'
                                            'Please perform a Background measurement')
                        else:
                            logger.warning('Open PMT shutter and close box lid')
                            self.board.MAX11210_setSelfCalGain(self.adc_gain_calibration)
                            self.board.MAX11210_setSelfCalOffset(self.adc_offset_calibration)
                    '''if (self.board.config.pmt_sys_offset_cal != 0):
                        self.board.MAX11210_setSysOffsetCal(self.board.config.pmt_sys_offset_cal)
                    else:
                        self.board.MAX11210_setSysOffsetCal(self.off_cal_val)
                    self.board.MAX11210_setSysGainCal(self.board.config.pmt_sys_gain_cal)
                    self.board.MAX11210_send_command(0b10001000)'''

                    adc_calibration = self.board.get_adc_calibration().to_dict()
                    logger.info('ADC calibration:\n%s' % adc_calibration)
                    step_log['ADC calibration'] = adc_calibration


                    # Launch PMT measure dialog.
                    delta_t = dt.timedelta(seconds=1)

                    # Set sampling reset_board_state
                    adc_rate = self.board.config.pmt_sampling_rate
                    # Construct a function compatible with `measure_dialog` to
                    # read from MAX11210 ADC.
                    data_func = (mrbox.ui.gtk.measure_dialog
                                 .adc_data_func_factory(proxy=self.board,
                                                        delta_t=delta_t,
                                                        adc_rate=adc_rate))

                    # Use constructed function to launch measurement dialog for
                    # the duration specified by the step options.
                    duration_s = (step_options.get('Measurement_duration_(s)')
                                  + 1)
                    use_si_prefixes = app_values.get('Use PMT y-axis SI '
                                                     'prefixes')
                    data = (mrbox.ui.gtk.measure_dialog
                            .measure_dialog(data_func, duration_s=duration_s,
                                            auto_start=True, auto_close=False,
                                            si_units=use_si_prefixes))
                    if data is not None:
                        # Append measured data as JSON line to [new-line
                        # delimited JSON][1] file for step.
                        #
                        # Each line of results can be loaded using
                        # `pandas.read_json(..., orient='split')`.
                        #
                        # [1]: http://ndjson.org/
                        filename = ph.path('PMT_readings-step%04d.ndjson' %
                                    app.protocol.current_step_number)
                        log_dir = app.experiment_log.get_log_path()
                        log_dir.makedirs_p()

                        data.name = filename.namebase

                        if step_label:
                            # Set name of data series based on step label.
                            data.name = step_label

                        with log_dir.joinpath(filename).open('a') as output:
                            # Write JSON data with `split` orientation, which
                            # preserves the name of the Pandas series.
                            data.to_json(output, orient='split')
                            output.write('\n')

                        step_log['data'] = data.to_dict()

                        self.update_excel_results()
                        logger.warning('Close PMT Shutter')
            except Exception:
                logger.error('[%s] Error applying step options.', __name__,
                             exc_info=True)
            finally:
                app.experiment_log.add_data(step_log, self.name)

        elif not self._user_warned:
            logger.warning('[%s] Cannot apply board settings since board is '
                           'not connected.', __name__, exc_info=True)
            # Do not warn user again until after the next connection attempt.
            self._user_warned = True

    def update_excel_results(self, launch=False):
        '''
        Update output Excel results file.

        .. versionadded:: 0.19

        Parameters
        ----------
        launch : bool, optional
            If ``True``, launch Excel spreadsheet after writing.
        '''
        app = get_app()
        log_dir = app.experiment_log.get_log_path()

        # Update Excel file with latest PMT results.
        output_path = log_dir.joinpath('PMT_readings.xlsx')
        data_files = list(log_dir.files('PMT_readings-*.ndjson'))

        if not data_files:
            logger.debug('No PMT readings files found.')
            return

        logger.info(TEMPLATE_PATH)

        def _threadsafe_write_results():
            logger.info(launch)
            while True:
                try:
                    _write_results(TEMPLATE_PATH, output_path, data_files)
                    if launch:
                        try:
                            output_path.launch()
                        except Exception:
                            pass
                    break
                except IOError as e:
                    logger.info("I/O error({0}): {1}".format(e.errno, e.strerror))
                    response = yesno('Error writing PMT summary to Excel '
                                     'spreadsheet output path: `%s`.\n\nTry '
                                     'again?' %output_path)
                    if response == gtk.RESPONSE_NO:
                        break

        # Schedule writing of results to occur in main GTK
        # thread in case confirmation dialog needs to be
        # displayed.
        gobject.idle_add(_threadsafe_write_results)



    def open_board_connection(self):
        '''
        Establish serial connection to MR-Box peripheral board.
        '''
        # Try to connect to peripheral board through serial connection.

        # XXX Try to connect multiple times.
        # See [issue 1][1] on the [MR-Box peripheral board firmware
        # project][2].
        #
        # [1]: https://github.com/wheeler-microfluidics/mr-box-peripheral-board.py/issues/1
        # [2]: https://github.com/wheeler-microfluidics/mr-box-peripheral-board.py
        retry_count = 2
        for i in xrange(retry_count):
            try:
                self.board.close()
                self.board = None
            except Exception:
                pass

            try:

                self.board = mrbox.SerialProxy()

                host_software_version = utility.Version.fromstring(
                    str(self.board.host_software_version))
                remote_software_version = utility.Version.fromstring(
                    str(self.board.remote_software_version))

                # Offer to reflash the firmware if the major and minor versions
                # are not not identical. If micro versions are different,
                # the firmware is assumed to be compatible. See [1]
                #
                # [1]: https://github.com/wheeler-microfluidics/base-node-rpc/
                #              issues/8
                if any([host_software_version.major !=
                        remote_software_version.major,
                        host_software_version.minor !=
                        remote_software_version.minor]):
                    response = yesno("The MR-box peripheral board 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()

                # Serial connection to peripheral **successfully established**.
                logger.info('Serial connection to peripheral board '
                            '**successfully established**')

                logger.info('Peripheral board properties:\n%s',
                            self.board.properties)
                logger.info('Reset board state to defaults.')
                break
            except (serial.SerialException, IOError):
                time.sleep(1)
        else:
            # Serial connection to peripheral **could not be established**.
            logger.warning('Serial connection to peripheral board could not '
                           'be established.')

    def on_edit_configuration(self, widget=None, data=None):
        '''
        Display a dialog to manually edit the configuration settings.
        '''
        config = self.board.config
        form = dict_to_form(config)
        dialog = FormViewDialog(form, 'Edit configuration settings')
        valid, response = dialog.run()
        if valid:
            self.board.update_config(**response)

    def on_flash_firmware(self, widget=None, data=None):
        app = get_app()
        try:
            self.board.flash_firmware()
            app.main_window_controller.info("Firmware updated successfully.",
                                            "Firmware update")
        except Exception, why:
            logger.error("Problem flashing firmware. ""%s" % why)
Exemplo n.º 17
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.º 18
0
#! -*- coding: utf-8 -*-
from flatland import Array, Boolean, Integer
from flatland.out import generic
from flatland.out.generic import Context


Unspecified = object()
Unique = object()
schema = Integer.named(u'number')
boolean_schema = Boolean.named(u'bool')
partial_anon_schema = Array.named(u'array').of(Integer)
full_anon_schema = Array.of(Integer)


def assert_bound_transform(fn, tagname, given, expected, **kw):
    return assert_transform(fn, tagname, given, expected, **kw)


def assert_unbound_transform(fn, tagname, given, expected, **kw):
    kw['bind'] = None
    return assert_transform(fn, tagname, given, expected, **kw)


def assert_transform(fn, tagname, given, expected,
                     context=Unspecified,
                     bind=Unspecified,
                     contents=Unspecified,
                     expected_contents=Unspecified):
    if context is Unspecified:
        context = Context()
    if bind is Unspecified:
Exemplo n.º 19
0
class TestPlugin(Plugin, AppDataController, StepOptionsController):
    """
    This class is automatically registered with the PluginManager.
    """
    implements(IPlugin)
    version = get_plugin_info(path(__file__).parent).version
    plugins_name = get_plugin_info(path(__file__).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
    '''

    serial_ports_ = [port for port in serial_device.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_),
    )
    '''
    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('led_on').using(default=False, optional=True), )

    def __init__(self):
        self.name = self.plugins_name
        self.proxy = 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.on_app_init()
        app_values = self.get_app_values()
        try:
            self.proxy = SerialProxy(port=app_values['serial_port'])
            self.proxy.pin_mode(pin=13, mode=1)
            logger.info('Connected to %s on port %s',
                        self.proxy.properties.display_name,
                        app_values['serial_port'])
        except Exception, e:
            logger.error('Could not connect to base-node-rpc on port %s: %s.',
                         app_values['serial_port'], e)
        if get_app().protocol:
            pgc = get_service_instance(ProtocolGridController, env='microdrop')
            pgc.update_grid()
Exemplo n.º 20
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.º 21
0
class DmfDeviceUiPlugin(AppDataController, StepOptionsController, Plugin,
                        pmh.BaseMqttReactor):
    """
    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

    AppFields = Form.of(
        String.named('video_config').using(default='',
                                           optional=True,
                                           properties={'show_in_gui': False}),
        String.named('surface_alphas').using(default='',
                                             optional=True,
                                             properties={'show_in_gui':
                                                         False}),
        String.named('canvas_corners').using(default='',
                                             optional=True,
                                             properties={'show_in_gui':
                                                         False}),
        String.named('frame_corners').using(default='',
                                            optional=True,
                                            properties={'show_in_gui': False}),
        Integer.named('x').using(default=None,
                                 optional=True,
                                 properties={'show_in_gui': False}),
        Integer.named('y').using(default=None,
                                 optional=True,
                                 properties={'show_in_gui': False}),
        Integer.named('width').using(default=400,
                                     optional=True,
                                     properties={'show_in_gui': False}),
        Integer.named('height').using(default=500,
                                      optional=True,
                                      properties={'show_in_gui': False}))

    StepFields = Form.of(
        Boolean.named('video_enabled').using(default=True,
                                             optional=True,
                                             properties={'title': 'Video'}))

    def __init__(self):
        self.name = self.plugin_name
        self.gui_process = None
        self.gui_heartbeat_id = None
        self._gui_enabled = False
        self.alive_timestamp = None
        self.should_terminate = False
        pmh.BaseMqttReactor.__init__(self)
        self.start()

    def reset_gui(self):
        py_exe = sys.executable
        # Set allocation based on saved app values (i.e., remember window size
        # and position from last run).
        app_values = self.get_app_values()
        allocation_args = ['-a', json.dumps(app_values)]

        app = get_app()
        if app.config.data.get('advanced_ui', False):
            debug_args = ['-d']
        else:
            debug_args = []

        self.gui_process = Popen(
            [py_exe, '-m', 'dmf_device_ui.bin.device_view', '-n', self.name] +
            allocation_args + debug_args + ['fixed', get_hub_uri()],
            creationflags=CREATE_NEW_PROCESS_GROUP)
        self.gui_process.daemon = False
        self._gui_enabled = True

        def keep_alive():
            if not self._gui_enabled:
                self.alive_timestamp = None
                return False
            elif self.gui_process.poll() == 0:
                # GUI process has exited.  Restart.
                self.cleanup()
                self.reset_gui()
                return False
            else:
                self.alive_timestamp = datetime.now()
                # Keep checking.
                return True

        # Go back to Undo 613 for working corners
        self.step_video_settings = None
        # Get current video settings from UI.
        app_values = self.get_app_values()
        # Convert JSON settings to 0MQ plugin API Python types.
        ui_settings = self.json_settings_as_python(app_values)

        self.set_ui_settings(ui_settings, default_corners=True)
        self.gui_heartbeat_id = gobject.timeout_add(1000, keep_alive)

    def cleanup(self):
        if self.gui_heartbeat_id is not None:
            gobject.source_remove(self.gui_heartbeat_id)

        self.alive_timestamp = None

    def get_schedule_requests(self, function_name):
        """
        Returns a list of scheduling requests (i.e., ScheduleRequest instances)
        for the function specified by function_name.
        """
        if function_name == 'on_plugin_enable':
            return [ScheduleRequest('droplet_planning_plugin', self.name)]
        elif function_name == 'on_dmf_device_swapped':
            # XXX Schedule `on_app_exit` handling before `device_info_plugin`,
            # since `hub_execute` uses the `device_info_plugin` service to
            # submit commands to through the 0MQ plugin hub.
            return [ScheduleRequest('microdrop.device_info_plugin', self.name)]
        elif function_name == 'on_app_exit':
            # XXX Schedule `on_app_exit` handling before `device_info_plugin`,
            # since `hub_execute` uses the `device_info_plugin` service to
            # submit commands to through the 0MQ plugin hub.
            return [ScheduleRequest(self.name, 'microdrop.device_info_plugin')]
        return []

    def on_app_exit(self):
        self.should_terminate = True
        self.mqtt_client.publish(
            'microdrop/dmf-device-ui-plugin/get-video-settings',
            json.dumps(None))

    def json_settings_as_python(self, json_settings):
        '''
        Convert DMF device UI plugin settings from json format to Python types.

        Python types are expected by DMF device UI plugin 0MQ command API.

        Args
        ----

            json_settings (dict) : DMF device UI plugin settings in
                JSON-compatible format (i.e., only basic Python data types).

        Returns
        -------

            (dict) : DMF device UI plugin settings in Python types expected by
                DMF device UI plugin 0MQ commands.
        '''
        py_settings = {}

        corners = dict([(k, json_settings.get(k))
                        for k in ('canvas_corners', 'frame_corners')])

        if all(corners.values()):
            # Convert CSV corners lists for canvas and frame to
            # `pandas.DataFrame` instances
            for k, v in corners.iteritems():
                # Prepend `'df_'` to key to indicate the type as a data frame.
                py_settings['df_' + k] = pd.read_csv(io.BytesIO(bytes(v)),
                                                     index_col=0)

        for k in ('video_config', 'surface_alphas'):
            if k in json_settings:
                if not json_settings[k]:
                    py_settings[k] = pd.Series(None)
                else:
                    py_settings[k] = pd.Series(json.loads(json_settings[k]))

        return py_settings

    def save_ui_settings(self, video_settings):
        '''
        Save specified DMF device UI 0MQ plugin settings to persistent
        Microdrop configuration (i.e., settings to be applied when Microdrop is
        launched).

        Args
        ----

            video_settings (dict) : DMF device UI plugin settings in
                JSON-compatible format returned by `get_ui_json_settings`
                method (i.e., only basic Python data types).
        '''
        app_values = self.get_app_values()
        # Select subset of app values that are present in `video_settings`.
        app_video_values = dict([(k, v) for k, v in app_values.iteritems()
                                 if k in video_settings.keys()])

        # If the specified video settings differ from app values, update
        # app values.
        if app_video_values != video_settings:
            app_values.update(video_settings)
            self.set_app_values(app_values)

    def set_ui_settings(self, ui_settings, default_corners=False):
        '''
        Set DMF device UI settings from settings dictionary.

        Args
        ----

            ui_settings (dict) : DMF device UI plugin settings in format
                returned by `json_settings_as_python` method.
        '''

        if 'video_config' in ui_settings:
            msg = {}
            msg['video_config'] = ui_settings['video_config'].to_json()
            self.mqtt_client.publish(
                'microdrop/dmf-device-ui-plugin/set-video-config',
                payload=json.dumps(msg),
                retain=True)

        if 'surface_alphas' in ui_settings:
            # TODO: Make Clear retained messages after exit
            msg = {}
            msg['surface_alphas'] = ui_settings['surface_alphas'].to_json()
            self.mqtt_client.publish(
                'microdrop/dmf-device-ui-plugin/set-surface-alphas',
                payload=json.dumps(msg),
                retain=True)

        if all((k in ui_settings)
               for k in ('df_canvas_corners', 'df_frame_corners')):
            # TODO: Test With Camera
            msg = {}
            msg['df_canvas_corners'] = ui_settings[
                'df_canvas_corners'].to_json()
            msg['df_frame_corners'] = ui_settings['df_frame_corners'].to_json()

            if default_corners:
                self.mqtt_client.publish(
                    'microdrop/dmf-device-ui-plugin/'
                    'set-default-corners',
                    payload=json.dumps(msg),
                    retain=True)
            else:
                self.mqtt_client.publish(
                    'microdrop/dmf-device-ui-plugin/'
                    'set-corners',
                    payload=json.dumps(msg),
                    retain=True)

    # #########################################################################
    # # Plugin signal handlers
    def on_connect(self, client, userdata, flags, rc):
        self.mqtt_client.subscribe(
            'microdrop/dmf-device-ui/get-video-settings')
        self.mqtt_client.subscribe('microdrop/dmf-device-ui/update-protocol')

    def on_message(self, client, userdata, msg):
        if msg.topic == 'microdrop/dmf-device-ui/get-video-settings':
            self.video_settings = json.loads(msg.payload)
            self.save_ui_settings(self.video_settings)
            if self.should_terminate:
                self.mqtt_client.publish(
                    'microdrop/dmf-device-ui-plugin/terminate')
        if msg.topic == 'microdrop/dmf-device-ui/update-protocol':
            self.update_protocol(json.loads(msg.payload))

    def on_plugin_disable(self):
        self._gui_enabled = False
        self.cleanup()

    def on_plugin_enable(self):
        super(DmfDeviceUiPlugin, self).on_plugin_enable()
        self.reset_gui()

        form = flatlandToDict(self.StepFields)
        self.mqtt_client.publish('microdrop/dmf-device-ui-plugin/schema',
                                 json.dumps(form),
                                 retain=True)
        defaults = {}
        for k, v in form.iteritems():
            defaults[k] = v['default']

        # defaults = map(lambda (k,v): {k: v['default']}, form.iteritems())
        self.mqtt_client.publish('microdrop/dmf-device-ui-plugin/step-options',
                                 json.dumps([defaults], cls=PandasJsonEncoder),
                                 retain=True)

    def on_step_removed(self, step_number, step):
        self.update_steps()

    def on_step_options_changed(self, plugin, step_number):
        self.update_steps()

    def on_step_run(self):
        '''
        Handler called whenever a step is executed.

        Plugins that handle this signal must emit the on_step_complete signal
        once they have completed the step. The protocol controller will wait
        until all plugins have completed the current step before proceeding.
        '''
        app = get_app()
        # TODO: Migrate video commands to mqtt!!
        # if (app.realtime_mode or app.running) and self.gui_process is not None:
        #     step_options = self.get_step_options()
        #     if not step_options['video_enabled']:
        #         hub_execute(self.name, 'disable_video',
        #                     wait_func=lambda *args: refresh_gui(), timeout_s=5,
        #                     silent=True)
        #     else:
        #         hub_execute(self.name, 'enable_video',
        #                     wait_func=lambda *args: refresh_gui(), timeout_s=5,
        #                     silent=True)
        emit_signal('on_step_complete', [self.name, None])

    def update_steps(self):
        app = get_app()
        num_steps = len(app.protocol.steps)

        protocol = []
        for i in range(num_steps):
            protocol.append(self.get_step_options(i))

        self.mqtt_client.publish('microdrop/dmf-device-ui-plugin/step-options',
                                 json.dumps(protocol, cls=PandasJsonEncoder),
                                 retain=True)

    def update_protocol(self, protocol):
        app = get_app()

        for i, s in enumerate(protocol):

            step = app.protocol.steps[i]
            prevData = step.get_data(self.plugin_name)
            values = {}

            for k, v in prevData.iteritems():
                if k in s:
                    values[k] = s[k]

            step.set_data(self.plugin_name, values)
            emit_signal('on_step_options_changed', [self.plugin_name, i],
                        interface=IPlugin)
Exemplo n.º 22
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)
Exemplo n.º 23
0
    def run(self, forms, initial_values=None):
        # Empty plugin form vbox
        # Get list of app option forms
        self.forms = forms
        self.form_views = {}
        self.clear_form()
        app = get_app()
        core_plugins_count = 0
        for name, form in self.forms.iteritems():
            # For each form, generate a pygtkhelpers formview and append the view
            # onto the end of the plugin vbox

            if len(form.field_schema) == 0:
                continue

            # Only include fields that do not have show_in_gui set to False in
            # 'properties' dictionary
            schema_entries = [f for f in form.field_schema\
                    if f.properties.get('show_in_gui', True)]
            gui_form = Form.of(*[
                Boolean.named(s.name).using(default=True, optional=True)
                for s in schema_entries
            ])
            FormView.schema_type = gui_form
            if not schema_entries:
                continue
            self.form_views[name] = FormView()
            if name in app.core_plugins:
                self.core_plugins_vbox.pack_start(self.form_views[name].widget)
                core_plugins_count += 1
            else:
                expander = gtk.Expander()
                expander.set_label(name)
                expander.set_expanded(True)
                expander.add(self.form_views[name].widget)
                self.plugin_form_vbox.pack_start(expander)
        if core_plugins_count == 0:
            self.frame_core_plugins.hide()
            self.plugin_form_vbox.remove(self.frame_core_plugins)
        else:
            if not self.frame_core_plugins in self.plugin_form_vbox.children():
                self.plugin_form_vbox.pack_start(self.frame_core_plugins)
            self.frame_core_plugins.show()

        if not initial_values:
            initial_values = {}

        for form_name, form in self.forms.iteritems():
            if not form.field_schema:
                continue
            form_view = self.form_views[form_name]
            values = initial_values.get(form_name, {})
            for name, field in form_view.form.fields.items():
                if name in values or not initial_values:
                    value = True
                else:
                    value = False
                logger.debug('set %s to %s' % (name, value))
                proxy = proxy_for(getattr(form_view, name))
                proxy.set_widget_value(value)
                field.label_widget.set_text(re.sub(r'_', ' ', name).title())

        self.dialog.show_all()

        response = self.dialog.run()
        if response == gtk.RESPONSE_OK:
            self.apply()
        elif response == gtk.RESPONSE_CANCEL:
            pass
        self.dialog.hide()
        return response
Exemplo n.º 24
0
    # TODO: CONTENT validation? can we do it here?
    *common_meta)

UserMetaSchema = DuckDict.named('UserMetaSchema').of(
    String.named(keys.CONTENTTYPE).validated_by(user_contenttype_validator),
    String.named(keys.EMAIL).using(optional=True),
    String.named(keys.OPENID).using(optional=True),
    String.named(keys.ENC_PASSWORD).using(optional=True),
    String.named(keys.RECOVERPASS_KEY).using(optional=True),
    String.named(keys.THEME_NAME).using(optional=True),
    String.named(keys.TIMEZONE).using(optional=True),
    String.named(keys.LOCALE).using(optional=True),
    String.named(keys.CSS_URL).using(optional=True),
    Integer.named(keys.RESULTS_PER_PAGE).using(optional=True),
    Integer.named(keys.EDIT_ROWS).using(optional=True),
    Boolean.named(keys.DISABLED).using(optional=True),
    Boolean.named(keys.WANT_TRIVIAL).using(optional=True),
    Boolean.named(keys.SHOW_COMMENTS).using(optional=True),
    Boolean.named(keys.EDIT_ON_DOUBLECLICK).using(optional=True),
    Boolean.named(keys.SCROLL_PAGE_AFTER_EDIT).using(optional=True),
    Boolean.named(keys.MAILTO_AUTHOR).using(optional=True),
    List.named(keys.QUICKLINKS).of(
        String.named('quicklinks')).using(optional=True),
    List.named(keys.SUBSCRIPTIONS).of(
        String.named('subscription').validated_by(
            subscription_validator)).using(optional=True),
    List.named(keys.EMAIL_SUBSCRIBED_EVENTS).of(
        String.named('email_subscribed_event')).using(optional=True),
    # TODO: DuckDict.named('bookmarks').using(optional=True),
    *common_meta)
Exemplo n.º 25
0
    def run(self, forms, initial_values=None):
        # Empty plugin form vbox
        # Get list of app option forms
        self.forms = forms
        self.form_views = {}
        self.clear_form()
        app = get_app()
        core_plugins_count = 0
        for name, form in self.forms.iteritems():
            # For each form, generate a pygtkhelpers formview and append the view
            # onto the end of the plugin vbox

            if len(form.field_schema) == 0:
                continue

            # Only include fields that do not have show_in_gui set to False in
            # 'properties' dictionary
            schema_entries = [f for f in form.field_schema\
                    if f.properties.get('show_in_gui', True)]
            gui_form = Form.of(*[Boolean.named(s.name).using(default=True,
                    optional=True) for s in schema_entries])
            FormView.schema_type = gui_form
            if not schema_entries:
                continue
            self.form_views[name] = FormView()
            if name in app.core_plugins:
                self.core_plugins_vbox.pack_start(self.form_views[name].widget)
                core_plugins_count += 1
            else:
                expander = gtk.Expander()
                expander.set_label(name)
                expander.set_expanded(True)
                expander.add(self.form_views[name].widget)
                self.plugin_form_vbox.pack_start(expander)
        if core_plugins_count == 0:
            self.frame_core_plugins.hide()
            self.plugin_form_vbox.remove(self.frame_core_plugins)
        else:
            if not self.frame_core_plugins in self.plugin_form_vbox.children():
                self.plugin_form_vbox.pack_start(self.frame_core_plugins)
            self.frame_core_plugins.show()

        if not initial_values:
            initial_values = {}

        for form_name, form in self.forms.iteritems():
            if not form.field_schema:
                continue
            form_view = self.form_views[form_name]
            values = initial_values.get(form_name, {})
            for name, field in form_view.form.fields.items():
                if name in values or not initial_values:
                    value = True
                else:
                    value = False
                logger.debug('set %s to %s' % (name, value))
                proxy = proxy_for(getattr(form_view, name))
                proxy.set_widget_value(value)
                field.label_widget.set_text(
                        re.sub(r'_',  ' ', name).title())

        self.dialog.show_all()

        response = self.dialog.run()
        if response == gtk.RESPONSE_OK:
            self.apply()
        elif response == gtk.RESPONSE_CANCEL:
            pass
        self.dialog.hide()
        return response
Exemplo n.º 26
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.º 27
0
class DmfDeviceUiPlugin(AppDataController, StepOptionsController, Plugin):
    """
    This class is automatically registered with the PluginManager.

    .. versionchanged:: 2.10
        Set default window size and position according to **screen size** *and*
        **window titlebar size**.  Also, force default window size if
        ``MICRODROP_FIRST_RUN`` environment variable is set to non-empty value.
    """
    implements(IPlugin)
    version = get_plugin_info(path(__file__).parent).version
    plugin_name = get_plugin_info(path(__file__).parent).plugin_name

    AppFields = Form.of(
        String.named('video_config').using(default='',
                                           optional=True,
                                           properties={'show_in_gui': False}),
        String.named('surface_alphas').using(default='',
                                             optional=True,
                                             properties={'show_in_gui':
                                                         False}),
        String.named('canvas_corners').using(default='',
                                             optional=True,
                                             properties={'show_in_gui':
                                                         False}),
        String.named('frame_corners').using(default='',
                                            optional=True,
                                            properties={'show_in_gui': False}),
        Integer.named('x').using(default=.5 * SCREEN_WIDTH,
                                 optional=True,
                                 properties={'show_in_gui': False}),
        Integer.named('y').using(default=SCREEN_TOP,
                                 optional=True,
                                 properties={'show_in_gui': False}),
        Integer.named('width').using(default=.5 * SCREEN_WIDTH,
                                     optional=True,
                                     properties={'show_in_gui': False}),
        Integer.named('height').using(default=SCREEN_HEIGHT -
                                      1.5 * TITLEBAR_HEIGHT,
                                      optional=True,
                                      properties={'show_in_gui': False}))

    StepFields = Form.of(
        Boolean.named('video_enabled').using(default=True,
                                             optional=True,
                                             properties={'title': 'Video'}))

    def __init__(self):
        self.name = self.plugin_name
        self.gui_process = None
        self.gui_heartbeat_id = None
        self._gui_enabled = False
        self.alive_timestamp = None

    def reset_gui(self):
        '''
        .. versionchanged:: 2.2.2
            Use :func:`pygtkhelpers.gthreads.gtk_threadsafe` decorator around
            function to wait for GUI process, rather than using
            :func:`gobject.idle_add`, to make intention clear.

        .. versionchanged:: 2.9
            Refresh list of registered commands once device UI process has
            started.  The list of registered commands is used to dynamically
            generate items in the device UI context menu.
        '''
        py_exe = sys.executable

        # Set allocation based on saved app values (i.e., remember window size
        # and position from last run).
        app_values = self.get_app_values()
        if os.environ.get('MICRODROP_FIRST_RUN'):
            # Use default options for window allocation.
            default_app_values = self.get_default_app_options()
            for k in ('x', 'y', 'width', 'height'):
                app_values[k] = default_app_values[k]

        allocation_args = ['-a', json.dumps(app_values)]

        app = get_app()
        if app.config.data.get('advanced_ui', False):
            debug_args = ['-d']
        else:
            debug_args = []

        self.gui_process = Popen(
            [py_exe, '-m', 'dmf_device_ui.bin.device_view', '-n', self.name] +
            allocation_args + debug_args + ['fixed', get_hub_uri()],
            creationflags=CREATE_NEW_PROCESS_GROUP)
        self._gui_enabled = True

        def keep_alive():
            if not self._gui_enabled:
                self.alive_timestamp = None
                return False
            elif self.gui_process.poll() == 0:
                # GUI process has exited.  Restart.
                self.cleanup()
                self.reset_gui()
                return False
            else:
                self.alive_timestamp = datetime.now()
                # Keep checking.
                return True

        self.step_video_settings = None

        @gtk_threadsafe
        def _wait_for_gui():
            self.wait_for_gui_process()
            # Get current video settings from UI.
            app_values = self.get_app_values()
            # Convert JSON settings to 0MQ plugin API Python types.
            ui_settings = self.json_settings_as_python(app_values)
            self.set_ui_settings(ui_settings, default_corners=True)
            self.gui_heartbeat_id = gobject.timeout_add(1000, keep_alive)
            # Refresh list of electrode and route commands.
            hub_execute('microdrop.command_plugin', 'get_commands')

        # Call as thread-safe function, since function uses GTK.
        _wait_for_gui()

    def cleanup(self):
        '''
        .. versionchanged:: 2.2.2
            Catch any exception encountered during GUI process termination.

        .. versionchanged:: 2.3.1
            Use :func:`kill_process_tree` to terminate DMF device UI process.

            This ensures any child processes of the UI process (e.g., video
            input process) are also killed.

            See also:
            https://stackoverflow.com/a/44648162/345236

        .. versionchanged:: 2.7
            Only try to terminate the GUI process if it is still running.
        '''
        logger.info('Stop DMF device UI keep-alive timer')
        if self.gui_heartbeat_id is not None:
            # Stop keep-alive polling of device UI process.
            gobject.source_remove(self.gui_heartbeat_id)
        if self.gui_process is not None and self.gui_process.poll() is None:
            logger.info('Terminate DMF device UI process')
            try:
                kill_process_tree(self.gui_process.pid)
                logger.info('Close DMF device UI process `%s`',
                            self.gui_process.pid)
            except Exception:
                logger.info(
                    'Unexpected error closing DMF device UI process '
                    '`%s`',
                    self.gui_process.pid,
                    exc_info=True)
        else:
            logger.info('No active DMF device UI process')
        self.alive_timestamp = None

    def wait_for_gui_process(self, retry_count=20, retry_duration_s=1):
        '''
        .. versionchanged:: 2.7.2
            Do not execute `refresh_gui()` while waiting for response from
            `hub_execute()`.
        '''
        start = datetime.now()
        for i in xrange(retry_count):
            try:
                hub_execute(self.name, 'ping', timeout_s=5, silent=True)
            except Exception:
                logger.debug('[wait_for_gui_process] failed (%d of %d)',
                             i + 1,
                             retry_count,
                             exc_info=True)
            else:
                logger.info('[wait_for_gui_process] success (%d of %d)', i + 1,
                            retry_count)
                self.alive_timestamp = datetime.now()
                return
            for j in xrange(10):
                time.sleep(retry_duration_s / 10.)
                refresh_gui()
        raise IOError('Timed out after %ss waiting for GUI process to connect '
                      'to hub.' % si_format(
                          (datetime.now() - start).total_seconds()))

    def get_schedule_requests(self, function_name):
        """
        Returns a list of scheduling requests (i.e., ScheduleRequest instances)
        for the function specified by function_name.

        .. versionchanged:: 2.3.3
            Do not submit ``on_app_exit`` schedule request.  This is no longer
            necessary since ``hub_execute`` listening socket is no longer
            closed by ``microdrop.device_info_plugin`` during ``on_app_exit``
            callback.

        .. versionadded:: 2.9
            Enable _after_ command plugin and zmq hub plugin.
        """
        if function_name == 'on_plugin_enable':
            return [
                ScheduleRequest(p, self.name)
                for p in ('microdrop.zmq_hub_plugin',
                          'microdrop.command_plugin',
                          'droplet_planning_plugin')
            ]
        return []

    def on_app_exit(self):
        logger.info('Get current video settings from DMF device UI plugin.')
        json_settings = self.get_ui_json_settings()
        self.save_ui_settings(json_settings)
        self._gui_enabled = False
        self.cleanup()

    # #########################################################################
    # # DMF device UI 0MQ plugin settings
    def get_ui_json_settings(self):
        '''
        Get current video settings from DMF device UI plugin.

        Returns
        -------

            (dict) : DMF device UI plugin settings in JSON-compatible format
                (i.e., only basic Python data types).


        .. versionchanged:: 2.7.2
            Do not execute `refresh_gui()` while waiting for response from
            `hub_execute()`.
        '''
        video_settings = {}

        # Try to request video configuration.
        try:
            video_config = hub_execute(self.name,
                                       'get_video_config',
                                       timeout_s=2)
        except IOError:
            logger.warning('Timed out waiting for device window size and '
                           'position request.')
        else:
            if video_config is not None:
                video_settings['video_config'] = video_config.to_json()
            else:
                video_settings['video_config'] = ''

        # Try to request allocation to save in app options.
        try:
            data = hub_execute(self.name, 'get_corners', timeout_s=2)
        except IOError:
            logger.warning('Timed out waiting for device window size and '
                           'position request.')
        else:
            if data:
                # Get window allocation settings (i.e., width, height, x, y).

                # Replace `df_..._corners` with CSV string named `..._corners`
                # (no `df_` prefix).
                for k in ('df_canvas_corners', 'df_frame_corners'):
                    if k in data:
                        data['allocation'][k[3:]] = data.pop(k).to_csv()
                video_settings.update(data['allocation'])

        # Try to request surface alphas.
        try:
            surface_alphas = hub_execute(self.name,
                                         'get_surface_alphas',
                                         timeout_s=2)
        except IOError:
            logger.warning('Timed out waiting for surface alphas.')
        else:
            if surface_alphas is not None:
                video_settings['surface_alphas'] = surface_alphas.to_json()
            else:
                video_settings['surface_alphas'] = ''
        return video_settings

    def get_ui_settings(self):
        '''
        Get current video settings from DMF device UI plugin.

        Returns
        -------

            (dict) : DMF device UI plugin settings in Python types expected by
                DMF device UI plugin 0MQ commands.
        '''
        json_settings = self.get_ui_json_settings()
        return self.json_settings_as_python(json_settings)

    def json_settings_as_python(self, json_settings):
        '''
        Convert DMF device UI plugin settings from json format to Python types.

        Python types are expected by DMF device UI plugin 0MQ command API.

        Args
        ----

            json_settings (dict) : DMF device UI plugin settings in
                JSON-compatible format (i.e., only basic Python data types).

        Returns
        -------

            (dict) : DMF device UI plugin settings in Python types expected by
                DMF device UI plugin 0MQ commands.
        '''
        py_settings = {}

        corners = dict([(k, json_settings.get(k))
                        for k in ('canvas_corners', 'frame_corners')])

        if all(corners.values()):
            # Convert CSV corners lists for canvas and frame to
            # `pandas.DataFrame` instances
            for k, v in corners.iteritems():
                # Prepend `'df_'` to key to indicate the type as a data frame.
                py_settings['df_' + k] = pd.read_csv(io.BytesIO(bytes(v)),
                                                     index_col=0)

        for k in ('video_config', 'surface_alphas'):
            if k in json_settings:
                if not json_settings[k]:
                    py_settings[k] = pd.Series(None)
                else:
                    py_settings[k] = pd.Series(json.loads(json_settings[k]))

        return py_settings

    def save_ui_settings(self, video_settings):
        '''
        Save specified DMF device UI 0MQ plugin settings to persistent
        Microdrop configuration (i.e., settings to be applied when Microdrop is
        launched).

        Args
        ----

            video_settings (dict) : DMF device UI plugin settings in
                JSON-compatible format returned by `get_ui_json_settings`
                method (i.e., only basic Python data types).
        '''
        app_values = self.get_app_values()
        # Select subset of app values that are present in `video_settings`.
        app_video_values = dict([(k, v) for k, v in app_values.iteritems()
                                 if k in video_settings.keys()])

        # If the specified video settings differ from app values, update
        # app values.
        if app_video_values != video_settings:
            app_values.update(video_settings)
            self.set_app_values(app_values)

    def set_ui_settings(self, ui_settings, default_corners=False):
        '''
        Set DMF device UI settings from settings dictionary.

        Args
        ----

            ui_settings (dict) : DMF device UI plugin settings in format
                returned by `json_settings_as_python` method.


        .. versionchanged:: 2.7.2
            Do not execute `refresh_gui()` while waiting for response from
            `hub_execute()`.
        '''
        if self.alive_timestamp is None or self.gui_process is None:
            # Repeat until GUI process has started.
            raise IOError('GUI process not ready.')

        if 'video_config' in ui_settings:
            hub_execute(self.name,
                        'set_video_config',
                        video_config=ui_settings['video_config'],
                        timeout_s=5)

        if 'surface_alphas' in ui_settings:
            hub_execute(self.name,
                        'set_surface_alphas',
                        surface_alphas=ui_settings['surface_alphas'],
                        timeout_s=5)

        if all((k in ui_settings)
               for k in ('df_canvas_corners', 'df_frame_corners')):
            if default_corners:
                hub_execute(self.name,
                            'set_default_corners',
                            canvas=ui_settings['df_canvas_corners'],
                            frame=ui_settings['df_frame_corners'],
                            timeout_s=5)
            else:
                hub_execute(self.name,
                            'set_corners',
                            df_canvas_corners=ui_settings['df_canvas_corners'],
                            df_frame_corners=ui_settings['df_frame_corners'],
                            timeout_s=5)

    # #########################################################################
    # # Plugin signal handlers
    def on_plugin_disable(self):
        self._gui_enabled = False
        self.cleanup()

    def on_plugin_enable(self):
        super(DmfDeviceUiPlugin, self).on_plugin_enable()
        self.reset_gui()

    def on_step_run(self):
        '''
        Handler called whenever a step is executed.

        Plugins that handle this signal must emit the on_step_complete signal
        once they have completed the step. The protocol controller will wait
        until all plugins have completed the current step before proceeding.

        .. versionchanged:: 2.2.2
            Emit ``on_step_complete`` signal within thread-safe function, since
            signal callbacks may use GTK.
        '''
        app = get_app()

        if (app.realtime_mode or app.running) and self.gui_process is not None:
            step_options = self.get_step_options()
            if not step_options['video_enabled']:
                command = 'disable_video'
            else:
                command = 'enable_video'

            hub_execute(self.name, command)

            # Call as thread-safe function, since signal callbacks may use GTK.
            gtk_threadsafe(emit_signal)('on_step_complete', [self.name, None])
Exemplo n.º 28
0
class App(SingletonPlugin, AppDataController):
    implements(IPlugin)
    '''
INFO:  <Plugin App 'microdrop.app'>
INFO:  <Plugin ConfigController 'microdrop.gui.config_controller'>
INFO:  <Plugin DmfDeviceController 'microdrop.gui.dmf_device_controller'>
INFO:  <Plugin ExperimentLogController
         'microdrop.gui.experiment_log_controller'>
INFO:  <Plugin MainWindowController 'microdrop.gui.main_window_controller'>
INFO:  <Plugin ProtocolController 'microdrop.gui.protocol_controller'>
INFO:  <Plugin ProtocolGridController 'microdrop.gui.protocol_grid_controller'>
    '''
    core_plugins = [
        'microdrop.app', 'microdrop.gui.config_controller',
        'microdrop.gui.dmf_device_controller',
        'microdrop.gui.experiment_log_controller',
        'microdrop.gui.main_window_controller',
        'microdrop.gui.protocol_controller',
        'microdrop.gui.protocol_grid_controller', 'wheelerlab.zmq_hub_plugin',
        'wheelerlab.electrode_controller_plugin',
        'wheelerlab.device_info_plugin'
    ]

    AppFields = Form.of(
        Integer.named('x').using(default=None,
                                 optional=True,
                                 properties={'show_in_gui': False}),
        Integer.named('y').using(default=None,
                                 optional=True,
                                 properties={'show_in_gui': False}),
        Integer.named('width').using(default=400,
                                     optional=True,
                                     properties={'show_in_gui': False}),
        Integer.named('height').using(default=500,
                                      optional=True,
                                      properties={'show_in_gui': False}),
        Enum.named('update_automatically'  #pylint: disable-msg=E1101,E1120
                   ).using(default=1, optional=True).valued(
                       'auto-update',
                       'check for updates, but ask before installing',
                       '''don't check for updates'''),
        String.named('server_url').using(  #pylint: disable-msg=E1120
            default='http://microfluidics.utoronto.ca/update',
            optional=True,
            properties=dict(show_in_gui=False)),
        Boolean.named('realtime_mode').using(  #pylint: disable-msg=E1120
            default=False,
            optional=True,
            properties=dict(show_in_gui=False)),
        Filepath.named('log_file').using(  #pylint: disable-msg=E1120
            default='',
            optional=True,
            properties={'action': gtk.FILE_CHOOSER_ACTION_SAVE}),
        Boolean.named('log_enabled').using(  #pylint: disable-msg=E1120
            default=False, optional=True),
        Enum.named('log_level').using(  #pylint: disable-msg=E1101, E1120
            default='info', optional=True).valued('debug', 'info', 'warning',
                                                  'error', 'critical'),
    )

    def __init__(self):
        args = parse_args()

        print 'Arguments: %s' % args

        self.name = "microdrop.app"
        # get the version number
        self.version = ""
        try:
            raise Exception
            version = subprocess.Popen(
                ['git', 'describe'],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                stdin=subprocess.PIPE).communicate()[0].rstrip()
            m = re.match('v(\d+)\.(\d+)-(\d+)', version)
            self.version = "%s.%s.%s" % (m.group(1), m.group(2), m.group(3))
            branch = subprocess.Popen(
                ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                stdin=subprocess.PIPE).communicate()[0].rstrip()
            if branch.strip() != 'master':
                self.version += "-%s" % branch
        except:
            import pkg_resources

            version = pkg_resources.get_distribution('microdrop').version

            dev = ('dev' in version)

            self.version = re.sub('\.dev.*', '', re.sub('post', '', version))
            if dev:
                self.version += "-dev"

        self.realtime_mode = False
        self.running = False
        self.builder = gtk.Builder()
        self.signals = {}
        self.plugin_data = {}

        # these members are initialized by plugins
        self.experiment_log_controller = None
        self.config_controller = None
        self.dmf_device_controller = None
        self.protocol_controller = None
        self.main_window_controller = None

        # Enable custom logging handler
        logging.getLogger().addHandler(CustomHandler())
        self.log_file_handler = None

        # config model
        try:
            self.config = Config(args.config)
        except IOError:
            logging.error(
                'Could not read configuration file, `%s`.  Make sure'
                ' it exists and is readable.', args.config)
            raise SystemExit(-1)

        # set the log level
        if self.name in self.config.data and ('log_level'
                                              in self.config.data[self.name]):
            self._set_log_level(self.config.data[self.name]['log_level'])
        logger.info('MicroDrop version: %s', self.version)
        logger.info('Running in working directory: %s', os.getcwd())

        # Run post install hooks for freshly installed plugins.
        # It is necessary to delay the execution of these hooks here due to
        # Windows file locking preventing the deletion of files that are in use.
        post_install_queue_path = \
            path(self.config.data['plugins']['directory']) \
            .joinpath('post_install_queue.yml')
        if post_install_queue_path.isfile():
            post_install_queue = yaml.load(post_install_queue_path.bytes())
            post_install_queue = map(path, post_install_queue)

            logger.info('[App] processing post install hooks.')
            for p in post_install_queue[:]:
                try:
                    info = get_plugin_info(p)
                    logger.info("  running post install hook for %s" %
                                info.plugin_name)
                    plugin_manager.post_install(p)
                except Exception:
                    logging.info(''.join(traceback.format_exc()))
                    logging.error('Error running post-install hook for %s.',
                                  p.name,
                                  exc_info=True)
                finally:
                    post_install_queue.remove(p)
            post_install_queue_path.write_bytes(yaml.dump(post_install_queue))

        # Delete paths that were marked during the uninstallation of a plugin.
        # It is necessary to delay the deletion until here due to Windows file
        # locking preventing the deletion of files that are in use.
        deletions_path = path(self.config.data['plugins']['directory'])\
                .joinpath('requested_deletions.yml')
        if deletions_path.isfile():
            requested_deletions = yaml.load(deletions_path.bytes())
            requested_deletions = map(path, requested_deletions)

            logger.info('[App] processing requested deletions.')
            for p in requested_deletions[:]:
                try:
                    if p != p.abspath():
                        logger.info(
                            '    (warning) ignoring path %s since it '
                            'is not absolute', p)
                        continue
                    if p.isdir():
                        info = get_plugin_info(p)
                        if info:
                            logger.info('  deleting %s' % p)
                            cwd = os.getcwd()
                            os.chdir(p.parent)
                            try:
                                path(p.name).rmtree()  #ignore_errors=True)
                            except Exception, why:
                                logger.warning('Error deleting path %s (%s)',
                                               p, why)
                                raise
                            os.chdir(cwd)
                            requested_deletions.remove(p)
                    else:  # if the directory doesn't exist, remove it from the
                        # list
                        requested_deletions.remove(p)
                except (AssertionError, ):
                    logger.info('  NOT deleting %s' % (p))
                    continue
                except (Exception, ):
                    logger.info('  NOT deleting %s' % (p))
                    continue
Exemplo n.º 29
0
    List.named(keys.ITEMTRANSCLUSIONS).of(String.named('itemtransclusion').validated_by(wikiname_validator)).using(optional=True),
    # TODO: CONTENT validation? can we do it here?
    *common_meta
)

UserMetaSchema = DuckDict.named('UserMetaSchema').of(
    String.named(keys.CONTENTTYPE).validated_by(user_contenttype_validator),
    String.named('email').using(optional=True),
    String.named('openid').using(optional=True),
    String.named('enc_password').using(optional=True),
    String.named('recoverpass_key').using(optional=True),
    String.named('theme_name').using(optional=True),
    String.named('timezone').using(optional=True),
    String.named('locale').using(optional=True),
    String.named('css_url').using(optional=True),
    Integer.named('results_per_page').using(optional=True),
    Integer.named('edit_rows').using(optional=True),
    Boolean.named('disabled').using(optional=True),
    Boolean.named('want_trivial').using(optional=True),
    Boolean.named('show_comments').using(optional=True),
    Boolean.named('edit_on_doubleclick').using(optional=True),
    Boolean.named('mailto_author').using(optional=True),
    List.named('quicklinks').of(String.named('quicklinks')).using(optional=True),
    List.named('subscribed_items').of(String.named('subscribed_item')).using(optional=True),
    List.named('email_subscribed_events').of(String.named('email_subscribed_event')).using(optional=True),
    #TODO: DuckDict.named('bookmarks').using(optional=True),
    *common_meta
)