diff --git a/.gitignore b/.gitignore index bc7453473705cbaf6a772e13df5d45c1d3891700..e6d04b42eef64d249acac03f0e7f752d9c9ac8da 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,5 @@ Breakpoints_v2.xcbkptlist *.pyc *.pyo *~ -/dstatInterface/dist/ -/dstatInterface/build/ -/dstat-interface/dstat-interface/dist/ -/dstat-interface/dstat-interface/build/ \ No newline at end of file +/dstat-interface/dist/ +/dstat-interface/build/ \ No newline at end of file diff --git a/dstat-interface/LICENSE b/LICENSE similarity index 100% rename from dstat-interface/LICENSE rename to LICENSE diff --git a/dstat-interface/README b/README similarity index 100% rename from dstat-interface/README rename to README diff --git a/dstat-interface/dstat-interface/__init__.py b/dstat-interface/__init__.py similarity index 100% rename from dstat-interface/dstat-interface/__init__.py rename to dstat-interface/__init__.py diff --git a/dstat-interface/dstat-interface/build_windows.py b/dstat-interface/build_windows.py similarity index 100% rename from dstat-interface/dstat-interface/build_windows.py rename to dstat-interface/build_windows.py diff --git a/dstat-interface/dstat-interface/drivers/VirtualSerial.inf b/dstat-interface/drivers/VirtualSerial.inf similarity index 100% rename from dstat-interface/dstat-interface/drivers/VirtualSerial.inf rename to dstat-interface/drivers/VirtualSerial.inf diff --git a/dstat-interface/dstat-interface.bat b/dstat-interface/dstat-interface.bat new file mode 100644 index 0000000000000000000000000000000000000000..da83e27ab1d09b0b02466c89f033c52a7621b484 --- /dev/null +++ b/dstat-interface/dstat-interface.bat @@ -0,0 +1 @@ +python .\main.py diff --git a/dstat-interface/dstat-interface/dstat.spec b/dstat-interface/dstat.spec similarity index 100% rename from dstat-interface/dstat-interface/dstat.spec rename to dstat-interface/dstat.spec diff --git a/dstat-interface/dstat-interface/dstatInterface.xcodeproj/project.pbxproj b/dstat-interface/dstatInterface.xcodeproj/project.pbxproj similarity index 100% rename from dstat-interface/dstat-interface/dstatInterface.xcodeproj/project.pbxproj rename to dstat-interface/dstatInterface.xcodeproj/project.pbxproj diff --git a/dstat-interface/dstat-interface/dstatInterface.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dstat-interface/dstatInterface.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from dstat-interface/dstat-interface/dstatInterface.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to dstat-interface/dstatInterface.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/dstat-interface/dstat-interface/dstatInterface.xcodeproj/project.xcworkspace/xcshareddata/dstatInterface.xccheckout b/dstat-interface/dstatInterface.xcodeproj/project.xcworkspace/xcshareddata/dstatInterface.xccheckout similarity index 100% rename from dstat-interface/dstat-interface/dstatInterface.xcodeproj/project.xcworkspace/xcshareddata/dstatInterface.xccheckout rename to dstat-interface/dstatInterface.xcodeproj/project.xcworkspace/xcshareddata/dstatInterface.xccheckout diff --git a/dstat-interface/dstat-interface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/dstatInterface.xcscheme b/dstat-interface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/dstatInterface.xcscheme similarity index 100% rename from dstat-interface/dstat-interface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/dstatInterface.xcscheme rename to dstat-interface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/dstatInterface.xcscheme diff --git a/dstat-interface/dstat-interface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/xcschememanagement.plist b/dstat-interface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 100% rename from dstat-interface/dstat-interface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/xcschememanagement.plist rename to dstat-interface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/dstat-interface/dstat-interface/dstat_comm.py b/dstat-interface/dstat_comm.py similarity index 75% rename from dstat-interface/dstat-interface/dstat_comm.py rename to dstat-interface/dstat_comm.py index 235cb4b2b530db7284c6eaf16e97b2bfc8c63bae..fab46b668b728a22e8a735cba4989b830960df3f 100644 --- a/dstat-interface/dstat-interface/dstat_comm.py +++ b/dstat-interface/dstat_comm.py @@ -22,6 +22,7 @@ from serial.tools import list_ports import time import struct import multiprocessing as mp +from errors import VarError def call_it(instance, name, args=(), kwargs=None): """Indirect caller for instance methods and multiprocessing. @@ -36,6 +37,44 @@ def call_it(instance, name, args=(), kwargs=None): kwargs = {} return getattr(instance, name)(*args, **kwargs) +def version_check(ser_port): + """Tries to contact DStat and get version. Returns a tuple of + (major, minor). If no response, returns empty tuple. + + Arguments: + ser_port -- address of serial port to use + """ + + ser = delayedSerial(ser_port, 1024000, timeout=1) + ser.write("ck") + + ser.flushInput() + ser.write('!') + + while not ser.read()=="C": + time.sleep(.5) + ser.write('!') + + + ser.write('V') + for line in ser: + if line.startswith('V'): + input = line.lstrip('V') + elif line.startswith("#"): + print line + elif line.lstrip().startswith("no"): + print line + ser.flushInput() + break + + parted = input.rstrip().split('.') + print parted + + ser.close() + + return (int(parted[0]), int(parted[1])) + + class delayedSerial(serial.Serial): """Extends Serial.write so that characters are output individually @@ -77,7 +116,17 @@ class Experiment(object): self.databytes = 8 self.data_extra = [] # must be defined even when not needed - self.__gaintable = [1e2, 3e2, 3e3, 3e4, 3e5, 3e6, 3e7, 5e8] + + major, minor = self.parameters['version'] + + if major >= 1: + if minor == 1: + self.__gaintable = [1e2, 3e2, 3e3, 3e4, 3e5, 3e6, 3e7, 5e8] + elif minor >= 2: + self.__gaintable = [1, 1e2, 3e3, 3e4, 3e5, 3e6, 3e7, 1e8] + else: + raise VarError(parameters['version'], "Invalid version parameter.") + self.gain = self.__gaintable[int(self.parameters['gain'])] self.commands = ["A", "G"] @@ -93,7 +142,7 @@ class Experiment(object): def run(self, ser_port): """Execute experiment. Connects and sends handshake signal to DStat - then sendsself.commands. Don't call directly as a process in Windows, + then sends self.commands. Don't call directly as a process in Windows, use run_wrapper instead. Arguments: @@ -113,6 +162,7 @@ class Experiment(object): self.serial.write(i) if not self.serial_handler(): + self.main_pipe.send("ABORT") break self.data_postprocessing() @@ -127,12 +177,18 @@ class Experiment(object): scan = 0 while True: if self.main_pipe.poll(): - print "abort" if self.main_pipe.recv() == 'a': self.serial.write('a') + print "ABORT!" return False for line in self.serial: + if self.main_pipe.poll(): + if self.main_pipe.recv() == 'a': + self.serial.write('a') + print "ABORT!" + return False + if line.startswith('B'): self.main_pipe.send(self.data_handler( (scan, self.serial.read(size=self.databytes)))) @@ -197,6 +253,54 @@ class Chronoamp(Experiment): return (scan, [seconds+milliseconds/1000., current*(1.5/self.gain/8388607)]) +class PDExp(Chronoamp): + """Photodiode/PMT experiment""" + def __init__(self, parameters, main_pipe): + super(Chronoamp, self).__init__(parameters, main_pipe) # Don't want to call CA's init + + self.datatype = "linearData" + self.xlabel = "Time (s)" + self.ylabel = "Current (A)" + self.data = [[], []] + self.datalength = 2 + self.databytes = 8 + self.xmin = 0 + self.xmax = self.parameters['time'] + + self.commands += "R" + self.commands[2] += "1" + self.commands[2] += " " + self.commands[2] += str(int(65536-self.parameters['voltage']*(65536./3000))) + self.commands[2] += " " + self.commands[2] += str(self.parameters['time']) + self.commands[2] += " " + +class PotExp(Experiment): + """Potentiometry experiment""" + def __init__(self, parameters, main_pipe): + super(PotExp, self).__init__(parameters, main_pipe) + + self.datatype = "linearData" + self.xlabel = "Time (s)" + self.ylabel = "Voltage (V)" + self.data = [[], []] + self.datalength = 2 + self.databytes = 8 + self.xmin = 0 + self.xmax = self.parameters['time'] + + self.commands += "P" + self.commands[2] += str(self.parameters['time']) + self.commands[2] += " 1 " #potentiometry mode + + def data_handler(self, data_input): + """Overrides Experiment method to not convert x axis to mV.""" + scan, data = data_input + # 2*uint16 + int32 + seconds, milliseconds, voltage = struct.unpack(' +# +# +# 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 . + +class Error(Exception): + """Copies Exception class""" + pass + +class InputError(Error): + """Exception raised for errors in the input. Extends Error class. + + Attributes: + expr -- input expression in which the error occurred + msg -- error message + """ + + def __init__(self, expr, msg): + self.expr = expr + self.msg = msg + +class VarError(Error): + """Exception raised for internal variable errors. Extends Error class. + + Attributes: + var -- var in which the error occurred + msg -- error message + """ + + def __init__(self, var, msg): + self.var = var + self.msg = msg \ No newline at end of file diff --git a/dstat-interface/dstat-interface/interface/__init__.py b/dstat-interface/interface/__init__.py similarity index 100% rename from dstat-interface/dstat-interface/interface/__init__.py rename to dstat-interface/interface/__init__.py diff --git a/dstat-interface/dstat-interface/interface/acv.glade b/dstat-interface/interface/acv.glade similarity index 100% rename from dstat-interface/dstat-interface/interface/acv.glade rename to dstat-interface/interface/acv.glade diff --git a/dstat-interface/dstat-interface/interface/adc_pot.glade b/dstat-interface/interface/adc_pot.glade similarity index 100% rename from dstat-interface/dstat-interface/interface/adc_pot.glade rename to dstat-interface/interface/adc_pot.glade diff --git a/dstat-interface/dstat-interface/interface/adc_pot.py b/dstat-interface/interface/adc_pot.py similarity index 61% rename from dstat-interface/dstat-interface/interface/adc_pot.py rename to dstat-interface/interface/adc_pot.py index 715b07a641a359af9924ce9d2bccacf40ca44921..e3888986f6c94e04ccd5b83282e1b26897102070 100644 --- a/dstat-interface/dstat-interface/interface/adc_pot.py +++ b/dstat-interface/interface/adc_pot.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # DStat Interface - An interface for the open hardware DStat potentiostat # Copyright (C) 2014 Michael D. M. Dryden - # Wheeler Microfluidics Laboratory @@ -19,6 +20,24 @@ import gtk +v1_1_gain = [(0, "100 Ω (15 mA FS)", 0), + (1, "300 Ω (5 mA FS)", 1), + (2, "3 kΩ (500 µA FS)", 2), + (3, "30 kΩ (50 µA FS)", 3), + (4, "300 kΩ (5 µA FS)", 4), + (5, "3 MΩ (500 nA FS)", 5), + (6, "30 MΩ (50 nA FS)", 6), + (7, "500 MΩ (3 nA FS)", 7)] + +v1_2_gain = [(0, "Bypass", 0), + (1, "100 Ω (15 mA FS)", 1), + (2, "3 kΩ (500 µA FS)", 2), + (3, "30 kΩ (50 µA FS)", 3), + (4, "300 kΩ (5 µA FS)", 4), + (5, "3 MΩ (500 nA FS)", 5), + (6, "30 MΩ (50 nA FS)", 6), + (7, "100 MΩ (15 nA FS)", 7)] + class adc_pot: def __init__(self): self.builder = gtk.Builder() @@ -40,6 +59,19 @@ class adc_pot: self.srate_combobox.set_active(7) self.gain_combobox = self.builder.get_object('gain_combobox') + self.gain_liststore = self.builder.get_object('gain_liststore') self.gain_combobox.pack_start(self.cell, True) self.gain_combobox.add_attribute(self.cell, 'text', 1) - self.gain_combobox.set_active(2) \ No newline at end of file + self.gain_combobox.set_active(2) + + def set_version(self, version): + """ Sets menus for DStat version. """ + self.gain_liststore.clear() + if version[0] == 1: + if version[1] == 1: + for i in v1_1_gain: + self.gain_liststore.append(i) + elif version[1] >= 2: + for i in v1_2_gain: + self.gain_liststore.append(i) + \ No newline at end of file diff --git a/dstat-interface/dstat-interface/interface/chronoamp.glade b/dstat-interface/interface/chronoamp.glade similarity index 100% rename from dstat-interface/dstat-interface/interface/chronoamp.glade rename to dstat-interface/interface/chronoamp.glade diff --git a/dstat-interface/dstat-interface/interface/cv.glade b/dstat-interface/interface/cv.glade similarity index 100% rename from dstat-interface/dstat-interface/interface/cv.glade rename to dstat-interface/interface/cv.glade diff --git a/dstat-interface/dstat-interface/interface/dpv.glade b/dstat-interface/interface/dpv.glade similarity index 99% rename from dstat-interface/dstat-interface/interface/dpv.glade rename to dstat-interface/interface/dpv.glade index 0716c176ff3d74120c2b7c949b6ac606466a9804..e13b41d2659b6db07b81fd40db8ea725f40ee45f 100644 --- a/dstat-interface/dstat-interface/interface/dpv.glade +++ b/dstat-interface/interface/dpv.glade @@ -464,9 +464,10 @@ True False - Note: The ADC converts continuously during DPV measurements and has priority over DAC changes. ADC sample rate should be significantly larger than DPV frequency for accurate timing. - center + Note: ADC samples 1/(ADC Frequency) before end of pulse. 1/(ADC frequency) should be significantly shorter than pulse period to reduce capacitive current. True + word-char + 43 True diff --git a/dstat-interface/dstat-interface/interface/dstatinterface.glade b/dstat-interface/interface/dstatinterface.glade similarity index 97% rename from dstat-interface/dstat-interface/interface/dstatinterface.glade rename to dstat-interface/interface/dstatinterface.glade index ee27bdf75c9a2817aecf517df73e972da20b33b9..8a43fb82c14bf3f86517d42992cf2beeb1310d82 100644 --- a/dstat-interface/dstat-interface/interface/dstatinterface.glade +++ b/dstat-interface/interface/dstatinterface.glade @@ -47,6 +47,11 @@ pde Photodiode + + 7 + pot + Potentiometry + @@ -58,7 +63,7 @@ 1.0 © Michael Dryden 2014 This software is licensed under the GNU GPL v3 - http://microfluidics.utoronto.ca + http://microfluidics.utoronto.ca/dstat GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -1210,6 +1215,36 @@ Thanks to Christian Fobel for help with Dropbot Plugin 2 + + + gtk-connect + True + True + True + True + + + + False + False + 3 + + + + + True + False + 0 + 5 + OCP: + True + + + False + False + 4 + + True @@ -1219,7 +1254,7 @@ Thanks to Christian Fobel for help with Dropbot Plugin True True - 3 + 5 diff --git a/dstat-interface/dstat-interface/interface/exp_int.py b/dstat-interface/interface/exp_int.py similarity index 93% rename from dstat-interface/dstat-interface/interface/exp_int.py rename to dstat-interface/interface/exp_int.py index 8e2e01c76c4d919e5f10abb8898f77815db2d709..7b75ee6a2195c76bed70327c3c40621adc53eee6 100644 --- a/dstat-interface/dstat-interface/interface/exp_int.py +++ b/dstat-interface/interface/exp_int.py @@ -200,5 +200,21 @@ class PD(ExpInterface): """Adds entry listings to superclass's self.entry dict""" super(PD, self).__init__('interface/pd.glade') - self.entry['voltage'] = self.builder.get_object('voltage_entry') + self.entry['voltage'] = self.builder.get_object('voltage_adjustment') + self.entry['time'] = self.builder.get_object('time_entry') + + def get_params(self): + """Returns a dict of parameters for experiment.""" + parameters = {} + parameters['voltage'] = int(self.entry['voltage'].get_value()) + parameters['time'] = int(self.entry['time'].get_text()) + + return parameters + +class POT(ExpInterface): + """Experiment class for Potentiometry.""" + def __init__(self): + """Adds entry listings to superclass's self.entry dict""" + super(POT, self).__init__('interface/potexp.glade') + self.entry['time'] = self.builder.get_object('time_entry') \ No newline at end of file diff --git a/dstat-interface/dstat-interface/interface/exp_window.py b/dstat-interface/interface/exp_window.py similarity index 98% rename from dstat-interface/dstat-interface/interface/exp_window.py rename to dstat-interface/interface/exp_window.py index 79301eb766719acba8b496130f4bf40a89ebf7b4..f5bf561f3f678a95d8c1cc7f6beb7a50d182ce33 100644 --- a/dstat-interface/dstat-interface/interface/exp_window.py +++ b/dstat-interface/interface/exp_window.py @@ -31,6 +31,7 @@ class Experiments: self.classes['dpv'] = exp.DPV() self.classes['acv'] = exp.ACV() self.classes['pde'] = exp.PD() + self.classes['pot'] = exp.POT() #fill exp_section exp_section = self.builder.get_object('exp_section_box') diff --git a/dstat-interface/dstat-interface/interface/lsv.glade b/dstat-interface/interface/lsv.glade similarity index 100% rename from dstat-interface/dstat-interface/interface/lsv.glade rename to dstat-interface/interface/lsv.glade diff --git a/dstat-interface/dstat-interface/interface/pd.glade b/dstat-interface/interface/pd.glade similarity index 84% rename from dstat-interface/dstat-interface/interface/pd.glade rename to dstat-interface/interface/pd.glade index a9e34be9cf87887bf799313f66c17c33d7967b26..5e87db990c80a655d1f07b792fb62816518918ef 100644 --- a/dstat-interface/dstat-interface/interface/pd.glade +++ b/dstat-interface/interface/pd.glade @@ -2,6 +2,11 @@ + + 3000 + 1 + 10 + False @@ -26,28 +31,6 @@ 2 2 True - - - True - True - - 8 - 0 - 1 - True - False - False - True - True - - - 1 - 2 - - GTK_FILL - 3 - - True @@ -94,6 +77,21 @@ GTK_FILL + + + True + True + voltage_adjustment + False + 100 + 1 + 0 + + + 1 + 2 + + False @@ -107,7 +105,6 @@ False 20 20 - Experiment not yet implemented @@ -118,6 +115,9 @@ 1 + + + diff --git a/dstat-interface/interface/potexp.glade b/dstat-interface/interface/potexp.glade new file mode 100644 index 0000000000000000000000000000000000000000..4d8d915234c08570bb877ed69bea97111c6f746d --- /dev/null +++ b/dstat-interface/interface/potexp.glade @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + False + 300 + 500 + + + True + True + automatic + automatic + + + True + False + none + + + True + False + + + True + False + 2 + 2 + + + True + False + Time (s) + + + 2 + + + + + True + True + + 5 + 0 + 1 + True + True + False + False + False + True + True + + + 1 + 2 + 2 + GTK_EXPAND + GTK_SHRINK + + + + + False + True + 5 + 0 + + + + + True + False + Connect the electrodes to the RE input and the W_SHIELD connectors. +The ADC's PGA can be used to amplify the input signal, but note that the plot's y-axis is only correct for PGA 2x. + True + 30 + + + True + True + 1 + + + + + + + + + + diff --git a/dstat-interface/dstat-interface/interface/save.py b/dstat-interface/interface/save.py similarity index 100% rename from dstat-interface/dstat-interface/interface/save.py rename to dstat-interface/interface/save.py diff --git a/dstat-interface/dstat-interface/interface/swv.glade b/dstat-interface/interface/swv.glade similarity index 99% rename from dstat-interface/dstat-interface/interface/swv.glade rename to dstat-interface/interface/swv.glade index 2f89f66d5cd4a35c2e85d26e0d67b7534c7d4111..62659c118e5f0fb1dd1d1e55d335cec7ba6946c8 100644 --- a/dstat-interface/dstat-interface/interface/swv.glade +++ b/dstat-interface/interface/swv.glade @@ -492,9 +492,10 @@ True False - Note: The ADC converts continuously during SWV measurements and has priority over DAC changes. ADC sample rate should be significantly larger than SWV frequency for accurate timing. - center + Note: ADC samples 1/(ADC Frequency) before end of pulse. ADC frequency should be significantly larger than SWV frequency to reduce capacitive current. True + word-char + 43 True diff --git a/dstat-interface/dstat-interface/interface_test.spec.bak b/dstat-interface/interface_test.spec.bak similarity index 100% rename from dstat-interface/dstat-interface/interface_test.spec.bak rename to dstat-interface/interface_test.spec.bak diff --git a/dstat-interface/dstat-interface/main.py b/dstat-interface/main.py old mode 100644 new mode 100755 similarity index 80% rename from dstat-interface/dstat-interface/main.py rename to dstat-interface/main.py index 434316977b4509d0f735cb27c0fc750fe2075fa8..0bf67c7855d8db993c6f924466e42bd4ac608f92 --- a/dstat-interface/dstat-interface/main.py +++ b/dstat-interface/main.py @@ -20,7 +20,8 @@ """ GUI Interface for Wheeler Lab DStat """ -import sys +import sys,os + try: import pygtk pygtk.require('2.0') @@ -38,33 +39,20 @@ except ImportError: print('gobject not available') sys.exit(1) +os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))) + import interface.save as save import dstat_comm as comm import interface.exp_window as exp_window import interface.adc_pot as adc_pot import plot import microdrop +from errors import InputError, VarError from serial import SerialException import multiprocessing import time -class Error(Exception): - """Copies Exception class""" - pass - -class InputError(Error): - """Exception raised for errors in the input. Extends Error class. - - Attributes: - expr -- input expression in which the error occurred - msg -- error message - """ - - def __init__(self, expr, msg): - self.expr = expr - self.msg = msg - class Main(object): """Main program """ def __init__(self): @@ -75,6 +63,7 @@ class Main(object): #create instance of interface components self.statusbar = self.builder.get_object('statusbar') + self.ocp_disp = self.builder.get_object('ocp_disp') self.window = self.builder.get_object('window1') self.aboutdialog = self.builder.get_object('aboutdialog1') self.rawbuffer = self.builder.get_object('databuffer1') @@ -124,13 +113,15 @@ class Main(object): self.spinner = self.builder.get_object('spinner') self.mainwindow = self.builder.get_object('window1') - self.mainwindow.set_title("Dstat Interface 0.1") + self.mainwindow.set_title("Dstat Interface 1.0") self.mainwindow.show_all() self.on_expcombobox_changed() self.expnumber = 0 + self.connected = False + self.menu_dropbot_connect = self.builder.get_object( 'menu_dropbot_connect') self.menu_dropbot_disconnect = self.builder.get_object( @@ -140,10 +131,12 @@ class Main(object): def on_window1_destroy(self, object, data=None): """ Quit when main window closed.""" + self.on_pot_stop_clicked() gtk.main_quit() def on_gtk_quit_activate(self, menuitem, data=None): """Quit when Quit selected from menu.""" + self.on_pot_stop_clicked() gtk.main_quit() def on_gtk_about_activate(self, menuitem, data=None): @@ -167,6 +160,47 @@ class Main(object): for i in self.serial_devices.ports: self.serial_liststore.append([i]) + + def on_serial_version_clicked(self, data=None): + """Retrieve DStat version.""" + try: + self.on_pot_stop_clicked() + except AttributeError: + pass + + self.version = comm.version_check(self.serial_liststore.get_value( + self.serial_combobox.get_active_iter(), 0)) + + self.statusbar.remove_all(self.error_context_id) + + if not len(self.version) == 2: + self.statusbar.push(self.error_context_id, "Communication Error") + return + + else: + self.adc_pot.set_version(self.version) + self.statusbar.push(self.error_context_id, + "".join(["DStat version: ", str(self.version[0]), + ".", str(self.version[1])]) + ) + self.start_ocp() + self.connected = True + + def start_ocp(self): + """Start OCP measurements.""" + if self.version[0] >= 1 and self.version[1] >= 2: + self.recv_p, self.send_p = multiprocessing.Pipe(duplex=True) + self.ocp_exp = comm.OCPExp(self.send_p) + + self.ocp_exp.run_wrapper(self.serial_liststore.get_value( + self.serial_combobox.get_active_iter(), 0)) + + self.send_p.close() # need for EOF signal to work + + self.ocp_proc = gobject.idle_add(self.ocp_running) + else: + print "OCP measurements not supported on v1.1 boards." + return def on_pot_start_clicked(self, data=None): """Run currently visible experiment.""" @@ -182,9 +216,15 @@ class Main(object): self.spinner.stop() self.startbutton.set_sensitive(True) self.stopbutton.set_sensitive(False) - + self.start_ocp() + + # Stop OCP measurements + self.on_pot_stop_clicked() + gobject.source_remove(self.ocp_proc) + selection = self.expcombobox.get_active() parameters = {} + parameters['version'] = self.version if self.adc_pot.buffer_toggle.get_active(): #True if box checked parameters['adc_buffer'] = "2" @@ -463,7 +503,67 @@ class Main(object): self.experiment_running_plot) gobject.idle_add(self.experiment_running) return + + elif selection == 6: # PD + parameters.update(self.exp_window.get_params('pde')) + + if (parameters['time'] <= 0): + raise InputError(parameters['clean_s'], + "Time must be greater than zero.") + if (parameters['time'] > 65535): + raise InputError(parameters['clean_s'], + "Time must fit in 16-bit counter.") + + self.recv_p, self.send_p = multiprocessing.Pipe(duplex=True) + self.current_exp = comm.PDExp(parameters, self.send_p) + + self.plot.clearall() + self.plot.changetype(self.current_exp) + self.current_exp.run_wrapper( + self.serial_liststore.get_value( + self.serial_combobox.get_active_iter(), 0)) + + self.send_p.close() + + self.plot_proc = gobject.timeout_add(200, + self.experiment_running_plot) + gobject.idle_add(self.experiment_running) + return + + elif selection == 7: # POT + if not (self.version[0] >= 1 and self.version[1] >= 2): + self.statusbar.push(self.error_context_id, + "v1.1 board does not support potentiometry.") + exceptions() + return + + parameters.update(self.exp_window.get_params('pot')) + + if (parameters['time'] <= 0): + raise InputError(parameters['clean_s'], + "Time must be greater than zero.") + if (parameters['time'] > 65535): + raise InputError(parameters['clean_s'], + "Time must fit in 16-bit counter.") + + self.recv_p, self.send_p = multiprocessing.Pipe(duplex=True) + self.current_exp = comm.PotExp(parameters, self.send_p) + + self.plot.clearall() + self.plot.changetype(self.current_exp) + + self.current_exp.run_wrapper( + self.serial_liststore.get_value( + self.serial_combobox.get_active_iter(), 0)) + + self.send_p.close() + + self.plot_proc = gobject.timeout_add(200, + self.experiment_running_plot) + gobject.idle_add(self.experiment_running) + return + else: self.statusbar.push(self.error_context_id, "Experiment not yet implemented.") @@ -519,6 +619,29 @@ class Main(object): self.experiment_done() return False + def ocp_running(self): + """Receive OCP value from experiment process and update ocp_disp field + + Returns: + True -- when experiment is continuing to keep function in GTK's queue. + False -- when experiment process signals EOFError or IOError to remove + function from GTK's queue. + """ + try: + if self.recv_p.poll(): + data = "".join(["OCP: ", + "{0:.3f}".format(self.recv_p.recv()), + " V"]) + self.ocp_disp.set_text(data) + + else: + time.sleep(.001) + return True + except EOFError: + return False + except IOError: + return False + def experiment_running_plot(self): """Plot all data in current_exp.data. Run in GTK main loop. Always returns True so must be manually @@ -578,12 +701,21 @@ class Main(object): self.spinner.stop() self.startbutton.set_sensitive(True) self.stopbutton.set_sensitive(False) + self.start_ocp() def on_pot_stop_clicked(self, data=None): """Stop current experiment. Signals experiment process to stop.""" - if self.recv_p: + try: print "stop" self.recv_p.send('a') + while True: + if self.recv_p.poll(): + if self.recv_p.recv() == "ABORT": + return + except AttributeError: + pass + except IOError: + pass def on_file_save_exp_activate(self, menuitem, data=None): """Activate dialogue to save current experiment data. """ @@ -604,7 +736,7 @@ class Main(object): "Waiting for µDrop to connect…") self.microdrop_proc = gobject.timeout_add(500, self.microdrop_listen) - def on_menu_dropbot_disconnect_activate(self, menuitem, data= None): + def on_menu_dropbot_disconnect_activate(self, menuitem=None, data=None): """Disconnect µDrop connection and stop listening.""" gobject.source_remove(self.microdrop_proc) self.microdrop.reset() @@ -612,6 +744,7 @@ class Main(object): self.dropbot_enabled = False self.menu_dropbot_connect.set_sensitive(True) self.menu_dropbot_disconnect.set_sensitive(False) + self.statusbar.push(self.message_context_id, "µDrop disconnected.") def microdrop_listen(self): """Manage signals from µDrop. Must be added to GTK's main loop to @@ -623,11 +756,19 @@ class Main(object): if data == microdrop.EXP_FINISH_REQ: if self.dropbot_triggered: - self.on_pot_start_clicked() - return False # Removes function from GTK's main loop + if self.connected: + self.on_pot_start_clicked() + else: + print ("WAR: µDrop requested experiment but DStat " + "disconnected.") + self.statusbar.push(self.message_context_id, + "Listen stopped—DStat disconnected.") + self.microdrop.reply(microdrop.EXPFINISHED) + self.on_menu_dropbot_disconnect_activate() + return False # Removes function from GTK's main loop else: - print "WAR: µDrop requested experiment finish confirmation \ - without starting experiment." + print ("WAR: µDrop requested experiment finish confirmation " + "without starting experiment.") self.microdrop.reply(microdrop.EXPFINISHED) elif data == microdrop.STARTEXP: diff --git a/dstat-interface/dstat-interface/microdrop.py b/dstat-interface/microdrop.py similarity index 91% rename from dstat-interface/dstat-interface/microdrop.py rename to dstat-interface/microdrop.py index f84df08bcf8d036ccf1324c1dffee48f4fd9c287..3970bfa0da25bee6fb1ad724ac907eec671a8125 100644 --- a/dstat-interface/dstat-interface/microdrop.py +++ b/dstat-interface/microdrop.py @@ -56,9 +56,11 @@ class microdropConnection(object): [1] -- The recieved message or "" if no message received. """ if self.state == SEND: - print "WAR: Microdrop Connection state invalid, resetting..." - self.reset() - self.__init__(self.port) + print "WAR: [uDrop-listen] Connection state invalid, resetting..." + # self.reset() + # self.__init__(self.port) + return (False, "") + try: message = self.soc.recv(flags=zmq.NOBLOCK, copy=True) self.state = SEND @@ -73,7 +75,7 @@ class microdropConnection(object): data -- a str to be sent """ if self.state == RECV: - print "WAR: Microdrop Connection state invalid, resetting..." + print "WAR: [uDrop-reply] Connection state invalid, resetting..." self.reset() self.__init__(self.port) return False diff --git a/dstat-interface/dstat-interface/plot.py b/dstat-interface/plot.py similarity index 100% rename from dstat-interface/dstat-interface/plot.py rename to dstat-interface/plot.py diff --git a/dstat-interface/dstat-interface/setup.py b/dstat-interface/setup.py similarity index 100% rename from dstat-interface/dstat-interface/setup.py rename to dstat-interface/setup.py