Zeromq plugin api

Add ZeroMQ plugin API to connect to Microdrop ZeroMQ plugin hub.

Merge request reports

Loading
+42 −42
Changes for dstat_interface/interface/save.py: 42 added lines, 42 removed lines.
Original line number Diff line number Diff line
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#     DStat Interface - An interface for the open hardware DStat potentiostat
#     Copyright (C) 2014  Michael D. M. Dryden - 
#     Copyright (C) 2014  Michael D. M. Dryden -
#     Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#         
#     
#
#
#     This program is free software: you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation, either version 3 of the License, or
#     (at your option) any later version.
#     
#
#     This program is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
#     
#
#     You should have received a copy of the GNU General Public License
#     along with this program.  If not, see <http://www.gnu.org/licenses/>.

@@ -32,22 +32,22 @@ def manSave(current_exp):
    fcd = gtk.FileChooserDialog("Save...", None, gtk.FILE_CHOOSER_ACTION_SAVE,
                                (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                 gtk.STOCK_SAVE, gtk.RESPONSE_OK))
    

    filters = [gtk.FileFilter()]
    filters[0].set_name("Space separated text (.txt)")
    filters[0].add_pattern("*.txt")
    

    fcd.set_do_overwrite_confirmation(True)
    for i in filters:
        fcd.add_filter(i)
                 

    response = fcd.run()
    

    if response == gtk.RESPONSE_OK:
        path = fcd.get_filename()
        _logger.error(" ".join(("Selected filepath:", path)),'INFO')
        filter_selection = fcd.get_filter().get_name()
        

        if filter_selection.endswith("(.npy)"):
            if (current_exp.parameters['shutter_true'] and current_exp.parameters['sync_true']):
                npy(current_exp, current_exp.data, "-".join((path,'data')))
@@ -61,7 +61,7 @@ def manSave(current_exp):
            else:
                text(current_exp, current_exp.data, path, auto=True)
        fcd.destroy()
        

    elif response == gtk.RESPONSE_CANCEL:
        fcd.destroy()

@@ -77,33 +77,33 @@ def plotSave(plots):
    filters.append(gtk.FileFilter())
    filters[1].set_name("Portable Network Graphics (.png)")
    filters[1].add_pattern("*.png")
    

    fcd.set_do_overwrite_confirmation(True)
    for i in filters:
        fcd.add_filter(i)
    

    response = fcd.run()
    

    if response == gtk.RESPONSE_OK:
        path = fcd.get_filename()
        _logger.error(" ".join(("Selected filepath:", path)),'INFO')
        filter_selection = fcd.get_filter().get_name()
        

        for i in plots:
            path += '-'
            path += i
        

            if filter_selection.endswith("(.pdf)"):
                if not path.endswith(".pdf"):
                    path += ".pdf"
            

            elif filter_selection.endswith("(.png)"):
                if not path.endswith(".png"):
                    path += ".png"
    

            plots[i].figure.savefig(path)  # determines format from file extension
        fcd.destroy()
    

    elif response == gtk.RESPONSE_CANCEL:
        fcd.destroy()

@@ -114,28 +114,28 @@ def man_param_save(window):
                                (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                 gtk.STOCK_SAVE, gtk.RESPONSE_OK)
                                )
    

    filters = [gtk.FileFilter()]
    filters[0].set_name("Parameter File (.yml)")
    filters[0].add_pattern("*.yml")
    

    fcd.set_do_overwrite_confirmation(True)
    for i in filters:
        fcd.add_filter(i)
                 

    response = fcd.run()
    

    if response == gtk.RESPONSE_OK:
        path = fcd.get_filename()
        _logger.error(" ".join(("Selected filepath:", path)),'INFO')
        

        if not path.endswith(".yml"):
            path += '.yml'
        

        save_params(window, path)

        fcd.destroy()
        

    elif response == gtk.RESPONSE_CANCEL:
        fcd.destroy()

@@ -146,24 +146,24 @@ def man_param_load(window):
                                (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                 gtk.STOCK_OPEN, gtk.RESPONSE_OK)
                                )
    

    filters = [gtk.FileFilter()]
    filters[0].set_name("Parameter File (.yml)")
    filters[0].add_pattern("*.yml")

    for i in filters:
        fcd.add_filter(i)
                 

    response = fcd.run()
    

    if response == gtk.RESPONSE_OK:
        path = fcd.get_filename()
        _logger.error(" ".join(("Selected filepath:", path)),'INFO')
        

        load_params(window, path)

        fcd.destroy()
        

    elif response == gtk.RESPONSE_CANCEL:
        fcd.destroy()

@@ -175,7 +175,7 @@ def autoSave(current_exp, dir_button, name, expnumber):
    path += name
    path += '-'
    path += str(expnumber)
    

    if (current_exp.parameters['shutter_true'] and current_exp.parameters['sync_true']):
        text(current_exp, current_exp.data, "-".join((path,'data')), auto=True)
        text(current_exp, current_exp.ftdata, "-".join((path,'ft')), auto=True)
@@ -186,7 +186,7 @@ def autoPlot(plots, dir_button, name, expnumber):
    for i in plots:
        if name == "":
            name = "file"
        

        path = dir_button.get_filename()
        path += '/'
        path += name
@@ -194,17 +194,17 @@ def autoPlot(plots, dir_button, name, expnumber):
        path += str(expnumber)
        path += '-'
        path += i
        

        if path.endswith(".pdf"):
            path = path.rstrip(".pdf")
    

        j = 1
        while os.path.exists("".join([path, ".pdf"])):
            if j > 1:
                path = path[:-len(str(j))]
            path += str(j)
            j += 1
    

        path += ".pdf"
        plots[i].figure.savefig(path)

@@ -225,25 +225,25 @@ def npy(exp, data, path, auto=False):
def text(exp, data, path, auto=False):
    if path.endswith(".txt"):
        path = path.rstrip(".txt")
    

    if auto == True:
        j = 1
        

        while os.path.exists("".join([path, ".txt"])):
            if j > 1:
                path = path[:-len(str(j))]
            path += str(j)
            j += 1
    

    path += ".txt"
    file = open(path, 'w')
    

    time = exp.time

    header = "".join(['#', time.isoformat(), "\n#"])
    for i in exp.commands:
        header += i
    

    try:
        if exp.ft_int:
            header += "\n"
@@ -266,5 +266,5 @@ def text(exp, data, path, auto=False):
        for row in col:
            file.write(str(row)+ "    ")
        file.write('\n')
    

    file.close()
+0 −0

File changed.

Preview size limit exceeded, changes collapsed.

+21 −19
Changes for dstat_interface/params.py: 21 added lines, 19 removed lines.
Original line number Diff line number Diff line
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#     DStat Interface - An interface for the open hardware DStat potentiostat
#     Copyright (C) 2014  Michael D. M. Dryden - 
#     Copyright (C) 2014  Michael D. M. Dryden -
#     Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#         
#     
#
#
#     This program is free software: you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation, either version 3 of the License, or
#     (at your option) any later version.
#     
#
#     This program is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
#     
#
#     You should have received a copy of the GNU General Public License
#     along with this program.  If not, see <http://www.gnu.org/licenses/>.

@@ -25,51 +25,53 @@ _logger = ErrorLogger(sender="dstat-interface-params")

def get_params(window):
    """Fetches and returns dict of all parameters for saving."""
    

    parameters = {}
    

    selection = window.exp_window.select_to_key[window.expcombobox.get_active()]
    parameters['experiment_index'] = selection
    

    try:
        parameters['version'] = window.version
    except AttributeError: # Will be thrown if not connected to DStat
        pass
    

    try:
        parameters.update(window.adc_pot.params)
    except InputError:
        _logger.error("No gain selected.", 'INFO')
    parameters.update(window.exp_window.get_params(selection))
    

    return parameters

def save_params(window, path):
    """Fetches current params and saves to path."""
    

    params = get_params(window)
    

    with open(path, 'w') as f:
        yaml.dump(params, f)
        

def load_params(window, path):
    """Loads params from a path into UI elements."""
    

    try:
        get_params(window)
    except InputError:  # Will be thrown because no experiment will be selected
        pass
    

    with open(path, 'r') as f:
        params = yaml.load(f)
    
    set_params(window, params)

def set_params(window, params):
    window.adc_pot.params = params
    

    if not 'experiment_index' in params:
        _logger.error("Missing experiment parameters.", 'WAR')
        return
    window.expcombobox.set_active(
                    window.exp_window.classes[params['experiment_index']][0])
    window.exp_window.set_params(params['experiment_index'], params)
    
    window.params_loaded = True
 No newline at end of file

    window.params_loaded = True
+123 −0
Changes for dstat_interface/plugin.py: 123 added lines, 0 removed lines.
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
import logging

from params import get_params, set_params, load_params, save_params
from zmq_plugin.plugin import Plugin as ZmqPlugin
from zmq_plugin.schema import decode_content_data
import gtk
import zmq

logger = logging.getLogger(__name__)


def get_hub_uri(default='tcp://localhost:31000', parent=None):
    message = 'Please enter 0MQ hub URI:'
    d = gtk.MessageDialog(parent=parent, flags=gtk.DIALOG_MODAL |
                          gtk.DIALOG_DESTROY_WITH_PARENT,
                          type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK_CANCEL,
                          message_format=message)
    entry = gtk.Entry()
    entry.set_text(default)
    d.vbox.pack_end(entry)
    d.vbox.show_all()
    entry.connect('activate', lambda _: d.response(gtk.RESPONSE_OK))
    d.set_default_response(gtk.RESPONSE_OK)

    r = d.run()
    text = entry.get_text().decode('utf8')
    d.destroy()
    if r == gtk.RESPONSE_OK:
        return text
    else:
        return None


class DstatPlugin(ZmqPlugin):
    '''
    Public 0MQ plugin API.
    '''
    def __init__(self, parent, *args, **kwargs):
        self.parent = parent
        super(DstatPlugin, self).__init__(*args, **kwargs)

    def check_sockets(self):
        '''
        Check for messages on command and subscription sockets and process
        any messages accordingly.
        '''
        try:
            msg_frames = self.command_socket.recv_multipart(zmq.NOBLOCK)
        except zmq.Again:
            pass
        else:
            self.on_command_recv(msg_frames)

        try:
            msg_frames = self.subscribe_socket.recv_multipart(zmq.NOBLOCK)
            source, target, msg_type, msg_json = msg_frames
            self.most_recent = msg_json
        except zmq.Again:
            pass
        except:
            logger.error('Error processing message from subscription '
                         'socket.', exc_info=True)
        return True

    def on_execute__load_params(self, request):
        '''
        Args
        ----

            params_path (str) : Path to file for parameters yaml file.
        '''
        data = decode_content_data(request)
        load_params(self.parent, data['params_path'])

    def on_execute__save_params(self, request):
        '''
        Args
        ----

            params_path (str) : Path to file for parameters yaml file.
        '''
        data = decode_content_data(request)
        save_params(self.parent, data['params_path'])

    def on_execute__set_params(self, request):
        '''
        Args
        ----

            (dict) : Parameters dictionary in format returned by `get_params`.
        '''
        data = decode_content_data(request)
        set_params(self.parent, data['params'])

    def on_execute__get_params(self, request):
        return get_params(self.parent)

    def on_execute__run_active_experiment(self, request):
        self.parent.statusbar.push(self.parent.message_context_id, "µDrop "
                                   "acquisition requested.")
        return self.parent.run_active_experiment()

    def on_execute__acquisition_complete(self, request):
        '''
        Args
        ----

        Returns
        -------

            (datetime.datetime or None) : The completion time of the experiment
                corresponding to the specified UUID.
        '''
        data = decode_content_data(request)
        self.parent.statusbar.push(self.parent.message_context_id, "µDrop "
                                   "notified of completed acquisition.")
        if data['experiment_id'] in self.parent.completed_experiment_ids:
            return self.parent.completed_experiment_ids[data['experiment_id']]
        elif data['experiment_id'] == self.parent.active_experiment_id:
            return None
        else:
            raise KeyError('Unknown experiment ID: %s' % data['experiment_id'])
+3 −3
Changes for pavement.py: 3 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -17,14 +17,14 @@ setup(name='dstat_interface',
      url='http://microfluidics.utoronto.ca/dstat',
      license='GPLv3',
      packages=['dstat_interface', ],
      install_requires=['matplotlib', 'numpy', 'pyserial', 
                        'pyzmq', 'pyyaml','seaborn'],
      install_requires=['matplotlib', 'numpy', 'pyserial', 'pyzmq',
                        'pyyaml','seaborn', 'zmq-plugin>=0.2.post2'],
      # Install data listed in `MANIFEST.in`
      include_package_data=True)


@task
@needs('generate_setup', 'minilib', 'setuptools.command.sdist') 
@needs('generate_setup', 'minilib', 'setuptools.command.sdist')
def sdist():
    """Overrides sdist to make sure that our setup.py is generated."""
    pass
Loading