Skip to content
...@@ -16,36 +16,62 @@ ...@@ -16,36 +16,62 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division, absolute_import, print_function, unicode_literals
import os import os
import sys import sys
import logging
import gtk try:
import gobject import gi
gi.require_version('Gtk', '3.0')
import dstat_comm from gi.repository import Gtk, GObject
except ImportError:
print("ERR: GTK not available")
sys.exit(1)
from ..dstat import comm, state
from ..experiments import (cal, chronoamp, cv, experiment_template,
idle, lsv, pot, swv)
import __main__ import __main__
from errors import InputError, VarError, ErrorLogger from ..errors import InputError, VarError
_logger = ErrorLogger(sender="dstat-interface-exp_int")
logger = logging.getLogger(__name__)
class ExpInterface(object): mod_dir = os.path.dirname(os.path.abspath(__file__))
class ExpInterface(GObject.Object):
"""Generic experiment interface class. Should be subclassed to implement """Generic experiment interface class. Should be subclassed to implement
experiment interfaces by populating self.entry. experiment interfaces by populating self.entry. Override class attributes
to set id and experiment class to run.
""" """
__gsignals__ = {
b'run_utility': (GObject.SIGNAL_RUN_FIRST, None, ()),
b'done_utility': (GObject.SIGNAL_RUN_FIRST, None, ())
}
id = None
experiment = None
def __init__(self, glade_path): def __init__(self, glade_path):
self.builder = gtk.Builder() super(ExpInterface, self).__init__()
self.builder = Gtk.Builder()
self.builder.add_from_file(glade_path) self.builder.add_from_file(glade_path)
self.builder.connect_signals(self) self.builder.connect_signals(self)
self.entry = {} # to be used only for str parameters self.entry = {} # to be used only for str parameters
self._params = None self._params = None
def get_experiment(self, parameters):
return self.__class__.experiment(parameters)
def _fill_params(self): def _fill_params(self):
self._params = dict.fromkeys(self.entry.keys()) self._params = dict.fromkeys(self.entry.keys())
@property @property
def params(self): def params(self):
"""Dict of parameters""" """Dict of parameters"""
if self._params == None: if self._params is None:
self._fill_params() self._fill_params()
self._get_params() self._get_params()
return self._params return self._params
...@@ -57,13 +83,13 @@ class ExpInterface(object): ...@@ -57,13 +83,13 @@ class ExpInterface(object):
@params.setter @params.setter
def params(self, params): def params(self, params):
if self._params == None: if self._params is None:
self._fill_params() self._fill_params()
for i in self._params: for i in self._params:
try: try:
self._params[i] = params[i] self._params[i] = params[i]
except KeyError as e: except KeyError as e:
_logger.error("Invalid parameter key: %s" % e, "WAR") logger.warning("Invalid parameter key: %s" % e)
self._set_params() self._set_params()
def _set_params(self): def _set_params(self):
...@@ -71,6 +97,14 @@ class ExpInterface(object): ...@@ -71,6 +97,14 @@ class ExpInterface(object):
for i in self.entry: for i in self.entry:
self.entry[i].set_text(self._params[i]) self.entry[i].set_text(self._params[i])
def get_window(self):
return self.builder.get_object('scrolledwindow1')
def on_run_utility(self, data=None):
self.emit('run_utility')
def on_done_utility(self, data=None):
self.emit('done_utility')
class Chronoamp(ExpInterface): class Chronoamp(ExpInterface):
"""Experiment class for chronoamperometry. Extends ExpInterface class to """Experiment class for chronoamperometry. Extends ExpInterface class to
support treeview neeeded for CA. support treeview neeeded for CA.
...@@ -80,22 +114,27 @@ class Chronoamp(ExpInterface): ...@@ -80,22 +114,27 @@ class Chronoamp(ExpInterface):
on_remove_button_clicked(self, widget) on_remove_button_clicked(self, widget)
get_params(self) get_params(self)
""" """
id = 'cae'
experiment = chronoamp.Chronoamp
def __init__(self): def __init__(self):
"""Extends superclass method to support treeview.""" """Extends superclass method to support treeview."""
super(Chronoamp, self).__init__('interface/chronoamp.glade') super(Chronoamp, self).__init__(os.path.join(mod_dir, 'chronoamp.glade'))
self.name = "Chronoamperometry"
self.statusbar = self.builder.get_object('statusbar') self.statusbar = self.builder.get_object('statusbar')
self.model = self.builder.get_object('ca_list') self.model = self.builder.get_object('ca_list')
self.treeview = self.builder.get_object('treeview') self.treeview = self.builder.get_object('treeview')
self.cell_renderer = gtk.CellRendererText() self.treeview.set_fixed_height_mode(False)
self.treeview.insert_column_with_attributes(-1, "Time", for i, column_title in enumerate(["Potential", "Time"]):
self.cell_renderer, text=1).set_expand(True) renderer = Gtk.CellRendererText()
self.treeview.insert_column_with_attributes(-1, "Potential", column = Gtk.TreeViewColumn(column_title, renderer, text=i)
self.cell_renderer, text=0).set_expand(True) column.set_expand(True)
self.treeview.append_column(column)
self.selection = self.treeview.get_selection() self.selection = self.treeview.get_selection()
self.selection.set_mode(gtk.SELECTION_MULTIPLE) self.selection.set_mode(Gtk.SelectionMode.MULTIPLE)
def _fill_params(self): def _fill_params(self):
super(Chronoamp, self)._fill_params() super(Chronoamp, self)._fill_params()
...@@ -112,9 +151,9 @@ class Chronoamp(ExpInterface): ...@@ -112,9 +151,9 @@ class Chronoamp(ExpInterface):
self.builder.get_object('potential_entry').get_text()) self.builder.get_object('potential_entry').get_text())
time = int(self.builder.get_object('time_entry').get_text()) time = int(self.builder.get_object('time_entry').get_text())
if (potential > 1499 or potential < -1500): if not state.board_instance.test_mv(potential):
raise ValueError("Potential out of range") raise ValueError("Potential out of range")
if (time < 1 or time > 65535): if not state.board_instance.test_s(time):
raise ValueError("Time out of range") raise ValueError("Time out of range")
self.model.append([potential, time]) self.model.append([potential, time])
...@@ -131,7 +170,7 @@ class Chronoamp(ExpInterface): ...@@ -131,7 +170,7 @@ class Chronoamp(ExpInterface):
referencelist = [] referencelist = []
for i in selected_rows: for i in selected_rows:
referencelist.append(gtk.TreeRowReference(self.model, i)) referencelist.append(Gtk.TreeRowReference(self.model, i))
for i in referencelist: for i in referencelist:
self.model.remove(self.model.get_iter(i.get_path())) self.model.remove(self.model.get_iter(i.get_path()))
...@@ -154,9 +193,12 @@ class Chronoamp(ExpInterface): ...@@ -154,9 +193,12 @@ class Chronoamp(ExpInterface):
class LSV(ExpInterface): class LSV(ExpInterface):
"""Experiment class for LSV.""" """Experiment class for LSV."""
id = 'lsv'
experiment = lsv.LSVExp
def __init__(self): def __init__(self):
"""Adds entry listings to superclass's self.entry dict""" """Adds entry listings to superclass's self.entry dict"""
super(LSV, self).__init__('interface/lsv.glade') super(LSV, self).__init__(os.path.join(mod_dir, 'lsv.glade'))
self.name = "Linear Sweep Voltammetry"
self.entry['clean_mV'] = self.builder.get_object('clean_mV') self.entry['clean_mV'] = self.builder.get_object('clean_mV')
self.entry['clean_s'] = self.builder.get_object('clean_s') self.entry['clean_s'] = self.builder.get_object('clean_s')
...@@ -168,9 +210,12 @@ class LSV(ExpInterface): ...@@ -168,9 +210,12 @@ class LSV(ExpInterface):
class CV(ExpInterface): class CV(ExpInterface):
"""Experiment class for CV.""" """Experiment class for CV."""
id = 'cve'
experiment = cv.CVExp
def __init__(self): def __init__(self):
"""Adds entry listings to superclass's self.entry dict""" """Adds entry listings to superclass's self.entry dict"""
super(CV, self).__init__('interface/cv.glade') super(CV, self).__init__(os.path.join(mod_dir, 'cv.glade'))
self.name = "Cyclic Voltammetry"
self.entry['clean_mV'] = self.builder.get_object('clean_mV') self.entry['clean_mV'] = self.builder.get_object('clean_mV')
self.entry['clean_s'] = self.builder.get_object('clean_s') self.entry['clean_s'] = self.builder.get_object('clean_s')
...@@ -184,9 +229,12 @@ class CV(ExpInterface): ...@@ -184,9 +229,12 @@ class CV(ExpInterface):
class SWV(ExpInterface): class SWV(ExpInterface):
"""Experiment class for SWV.""" """Experiment class for SWV."""
id = 'swv'
experiment = swv.SWVExp
def __init__(self): def __init__(self):
"""Adds entry listings to superclass's self.entry dict""" """Adds entry listings to superclass's self.entry dict"""
super(SWV, self).__init__('interface/swv.glade') super(SWV, self).__init__(os.path.join(mod_dir, 'swv.glade'))
self.name = "Square Wave Voltammetry"
self.entry['clean_mV'] = self.builder.get_object('clean_mV') self.entry['clean_mV'] = self.builder.get_object('clean_mV')
self.entry['clean_s'] = self.builder.get_object('clean_s') self.entry['clean_s'] = self.builder.get_object('clean_s')
...@@ -220,9 +268,13 @@ class SWV(ExpInterface): ...@@ -220,9 +268,13 @@ class SWV(ExpInterface):
class DPV(ExpInterface): class DPV(ExpInterface):
"""Experiment class for DPV.""" """Experiment class for DPV."""
id = 'dpv'
experiment = swv.DPVExp
def __init__(self): def __init__(self):
"""Adds entry listings to superclass's self.entry dict""" """Adds entry listings to superclass's self.entry dict"""
super(DPV, self).__init__('interface/dpv.glade') super(DPV, self).__init__(os.path.join(mod_dir, 'dpv.glade'))
self.name = "Differential Pulse Voltammetry"
self.entry['clean_mV'] = self.builder.get_object('clean_mV') self.entry['clean_mV'] = self.builder.get_object('clean_mV')
self.entry['clean_s'] = self.builder.get_object('clean_s') self.entry['clean_s'] = self.builder.get_object('clean_s')
...@@ -235,23 +287,29 @@ class DPV(ExpInterface): ...@@ -235,23 +287,29 @@ class DPV(ExpInterface):
self.entry['period'] = self.builder.get_object('period_entry') self.entry['period'] = self.builder.get_object('period_entry')
self.entry['width'] = self.builder.get_object('width_entry') self.entry['width'] = self.builder.get_object('width_entry')
class ACV(ExpInterface): # class ACV(ExpInterface):
"""Experiment class for ACV.""" # """Experiment class for ACV."""
def __init__(self): # id = 'acv'
"""Adds entry listings to superclass's self.entry dict""" # def __init__(self):
super(ACV, self).__init__('interface/acv.glade') # """Adds entry listings to superclass's self.entry dict"""
# super(ACV, self).__init__('interface/acv.glade')
self.entry['start'] = self.builder.get_object('start_entry') # self.name = "AC Voltammetry"
self.entry['stop'] = self.builder.get_object('stop_entry') #
self.entry['slope'] = self.builder.get_object('slope_entry') # self.entry['start'] = self.builder.get_object('start_entry')
self.entry['amplitude'] = self.builder.get_object('amplitude_entry') # self.entry['stop'] = self.builder.get_object('stop_entry')
self.entry['freq'] = self.builder.get_object('freq_entry') # self.entry['slope'] = self.builder.get_object('slope_entry')
# self.entry['amplitude'] = self.builder.get_object('amplitude_entry')
# self.entry['freq'] = self.builder.get_object('freq_entry')
class PD(ExpInterface): class PD(ExpInterface):
"""Experiment class for PD.""" """Experiment class for PD."""
id = 'pde'
experiment = chronoamp.PDExp
def __init__(self): def __init__(self):
"""Adds entry listings to superclass's self.entry dict""" """Adds entry listings to superclass's self.entry dict"""
super(PD, self).__init__('interface/pd.glade') super(PD, self).__init__(os.path.join(mod_dir, 'pd.glade'))
self.name = "Photodiode/PMT"
self.entry['time'] = self.builder.get_object('time_entry') self.entry['time'] = self.builder.get_object('time_entry')
self.entry['sync_freq'] = self.builder.get_object('sync_freq') self.entry['sync_freq'] = self.builder.get_object('sync_freq')
...@@ -312,16 +370,16 @@ class PD(ExpInterface): ...@@ -312,16 +370,16 @@ class PD(ExpInterface):
try: try:
self.builder.get_object('light_label').set_text(str( self.builder.get_object('light_label').set_text(str(
dstat_comm.read_light_sensor())) dstat_comm.read_light_sensor()))
dstat_comm.read_settings() comm.read_settings()
dstat_comm.settings['tcs_enabled'][1] = '1' # Make sure TCS enabled state.settings['tcs_enabled'][1] = '1' # Make sure TCS enabled
dstat_comm.write_settings() comm.write_settings()
self.builder.get_object('threshold_entry').set_text(str( self.builder.get_object('threshold_entry').set_text(str(
dstat_comm.settings['tcs_clear_threshold'][1])) state.settings['tcs_clear_threshold'][1]))
__main__.MAIN.start_ocp() __main__.MAIN.start_ocp()
finally: finally:
gobject.timeout_add(700, restore_buttons, self.buttons) GObject.timeout_add(700, restore_buttons, self.buttons)
def on_threshold_button_clicked(self, data=None): def on_threshold_button_clicked(self, data=None):
__main__.MAIN.on_pot_stop_clicked() __main__.MAIN.on_pot_stop_clicked()
...@@ -331,16 +389,16 @@ class PD(ExpInterface): ...@@ -331,16 +389,16 @@ class PD(ExpInterface):
i.set_sensitive(False) i.set_sensitive(False)
try: try:
dstat_comm.settings['tcs_clear_threshold'][1] = self.builder.get_object( state.settings['tcs_clear_threshold'][1] = self.builder.get_object(
'threshold_entry').get_text() 'threshold_entry').get_text()
dstat_comm.write_settings() comm.write_settings()
dstat_comm.read_settings() comm.read_settings()
self.builder.get_object('threshold_entry').set_text( self.builder.get_object('threshold_entry').set_text(
str(dstat_comm.settings['tcs_clear_threshold'][1])) str(state.settings['tcs_clear_threshold'][1]))
__main__.MAIN.start_ocp() __main__.MAIN.start_ocp()
finally: finally:
gobject.timeout_add(700, restore_buttons, self.buttons) GObject.timeout_add(700, restore_buttons, self.buttons)
def on_shutter_button_toggled(self, widget): def on_shutter_button_toggled(self, widget):
if self.bool['shutter_true'].get_active(): if self.bool['shutter_true'].get_active():
...@@ -353,17 +411,23 @@ class PD(ExpInterface): ...@@ -353,17 +411,23 @@ class PD(ExpInterface):
class POT(ExpInterface): class POT(ExpInterface):
"""Experiment class for Potentiometry.""" """Experiment class for Potentiometry."""
id = 'pot'
experiment = pot.PotExp
def __init__(self): def __init__(self):
"""Adds entry listings to superclass's self.entry dict""" """Adds entry listings to superclass's self.entry dict"""
super(POT, self).__init__('interface/potexp.glade') super(POT, self).__init__(os.path.join(mod_dir, 'potexp.glade'))
self.name = "Potentiometry"
self.entry['time'] = self.builder.get_object('time_entry') self.entry['time'] = self.builder.get_object('time_entry')
class CAL(ExpInterface): class CAL(ExpInterface):
"""Experiment class for Calibrating gain.""" """Experiment class for Calibrating gain."""
id = 'cal'
experiment = cal.CALExp
def __init__(self): def __init__(self):
"""Adds entry listings to superclass's self.entry dict""" """Adds entry listings to superclass's self.entry dict"""
super(CAL, self).__init__('interface/calib.glade') super(CAL, self).__init__(os.path.join(mod_dir, 'calib.glade'))
self.name = "Calilbration"
self.entry['time'] = self.builder.get_object('time_entry') self.entry['time'] = self.builder.get_object('time_entry')
self.entry['R100'] = self.builder.get_object('100_entry') self.entry['R100'] = self.builder.get_object('100_entry')
...@@ -385,27 +449,27 @@ class CAL(ExpInterface): ...@@ -385,27 +449,27 @@ class CAL(ExpInterface):
try: try:
__main__.MAIN.on_pot_stop_clicked() __main__.MAIN.on_pot_stop_clicked()
__main__.MAIN.stop_ocp() __main__.MAIN.stop_ocp()
dstat_comm.read_settings() comm.read_settings()
self.entry['R100'].set_text(str( self.entry['R100'].set_text(str(
dstat_comm.settings['r100_trim'][1])) state.settings['r100_trim'][1]))
self.entry['R3k'].set_text(str( self.entry['R3k'].set_text(str(
dstat_comm.settings['r3k_trim'][1])) state.settings['r3k_trim'][1]))
self.entry['R30k'].set_text(str( self.entry['R30k'].set_text(str(
dstat_comm.settings['r30k_trim'][1])) state.settings['r30k_trim'][1]))
self.entry['R300k'].set_text(str( self.entry['R300k'].set_text(str(
dstat_comm.settings['r300k_trim'][1])) state.settings['r300k_trim'][1]))
self.entry['R3M'].set_text(str( self.entry['R3M'].set_text(str(
dstat_comm.settings['r3M_trim'][1])) state.settings['r3M_trim'][1]))
self.entry['R30M'].set_text(str( self.entry['R30M'].set_text(str(
dstat_comm.settings['r30M_trim'][1])) state.settings['r30M_trim'][1]))
self.entry['R100M'].set_text(str( self.entry['R100M'].set_text(str(
dstat_comm.settings['r100M_trim'][1])) state.settings['r100M_trim'][1]))
__main__.MAIN.start_ocp() __main__.MAIN.start_ocp()
finally: finally:
gobject.timeout_add(700, restore_buttons, self.buttons) GObject.timeout_add(700, restore_buttons, self.buttons)
def on_write_button_clicked(self, data=None): def on_write_button_clicked(self, data=None):
for i in self.buttons: for i in self.buttons:
...@@ -415,23 +479,23 @@ class CAL(ExpInterface): ...@@ -415,23 +479,23 @@ class CAL(ExpInterface):
__main__.MAIN.on_pot_stop_clicked() __main__.MAIN.on_pot_stop_clicked()
__main__.MAIN.stop_ocp() __main__.MAIN.stop_ocp()
dstat_comm.settings['r100_trim'][1] = self.entry['R100'].get_text() state.settings['r100_trim'][1] = self.entry['R100'].get_text()
dstat_comm.settings['r3k_trim'][1] = self.entry['R3k'].get_text() state.settings['r3k_trim'][1] = self.entry['R3k'].get_text()
dstat_comm.settings['r30k_trim'][1] = self.entry['R30k'].get_text() state.settings['r30k_trim'][1] = self.entry['R30k'].get_text()
dstat_comm.settings['r300k_trim'][1] = self.entry['R300k'].get_text() state.settings['r300k_trim'][1] = self.entry['R300k'].get_text()
dstat_comm.settings['r3M_trim'][1] = self.entry['R3M'].get_text() state.settings['r3M_trim'][1] = self.entry['R3M'].get_text()
dstat_comm.settings['r30M_trim'][1] = self.entry['R30M'].get_text() state.settings['r30M_trim'][1] = self.entry['R30M'].get_text()
dstat_comm.settings['r100M_trim'][1] = self.entry['R100M'].get_text() state.settings['r100M_trim'][1] = self.entry['R100M'].get_text()
dstat_comm.write_settings() comm.write_settings()
__main__.MAIN.start_ocp() __main__.MAIN.start_ocp()
finally: finally:
gobject.timeout_add(700, restore_buttons, self.buttons) GObject.timeout_add(700, restore_buttons, self.buttons)
def on_measure_button_clicked(self, data=None): def on_measure_button_clicked(self, data=None):
if (int(self.entry['time'].get_text()) <= 0 or int(self.entry['time'].get_text()) > 65535): if int(self.entry['time'].get_text()) <= 0 or int(self.entry['time'].get_text()) > 65535:
print "ERR: Time out of range" logger.error("ERR: Time out of range")
return return
for i in self.buttons: for i in self.buttons:
...@@ -441,34 +505,35 @@ class CAL(ExpInterface): ...@@ -441,34 +505,35 @@ class CAL(ExpInterface):
__main__.MAIN.stop_ocp() __main__.MAIN.stop_ocp()
__main__.MAIN.spinner.start() __main__.MAIN.spinner.start()
offset = dstat_comm.measure_offset(self.get_params()['time']) offset = cal.measure_offset(self.params['time'])
for i in offset: for i in offset:
_logger.error(" ".join((i, str(-offset[i]))), "INFO") logger.info("{} {}".format(i, str(-offset[i])))
dstat_comm.settings[i][1] = str(-offset[i]) state.settings[i][1] = str(-offset[i])
self.entry['R100'].set_text(str( self.entry['R100'].set_text(str(
dstat_comm.settings['r100_trim'][1])) state.settings['r100_trim'][1]))
self.entry['R3k'].set_text(str( self.entry['R3k'].set_text(str(
dstat_comm.settings['r3k_trim'][1])) state.settings['r3k_trim'][1]))
self.entry['R30k'].set_text(str( self.entry['R30k'].set_text(str(
dstat_comm.settings['r30k_trim'][1])) state.settings['r30k_trim'][1]))
self.entry['R300k'].set_text(str( self.entry['R300k'].set_text(str(
dstat_comm.settings['r300k_trim'][1])) state.settings['r300k_trim'][1]))
self.entry['R3M'].set_text(str( self.entry['R3M'].set_text(str(
dstat_comm.settings['r3M_trim'][1])) state.settings['r3M_trim'][1]))
self.entry['R30M'].set_text(str( self.entry['R30M'].set_text(str(
dstat_comm.settings['r30M_trim'][1])) state.settings['r30M_trim'][1]))
self.entry['R100M'].set_text(str( self.entry['R100M'].set_text(str(
dstat_comm.settings['r100M_trim'][1])) state.settings['r100M_trim'][1]))
__main__.MAIN.start_ocp() __main__.MAIN.start_ocp()
finally: finally:
gobject.timeout_add(700, restore_buttons, self.buttons) GObject.timeout_add(700, restore_buttons, self.buttons)
__main__.MAIN.spinner.stop() __main__.MAIN.spinner.stop()
def restore_buttons(buttons): def restore_buttons(buttons):
""" Should be called with gobject callback """ """ Should be called with GObject callback """
for i in buttons: for i in buttons:
i.set_sensitive(True) i.set_sensitive(True)
......
...@@ -17,38 +17,93 @@ ...@@ -17,38 +17,93 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import interface.exp_int as exp import inspect
import logging
from collections import OrderedDict
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
except ImportError:
print "ERR: GTK not available"
sys.exit(1)
from . import exp_int
logger = logging.getLogger(__name__)
class Experiments(GObject.Object):
__gsignals__ = {
'run_utility': (GObject.SIGNAL_RUN_FIRST, None, ()),
'done_utility': (GObject.SIGNAL_RUN_FIRST, None, ())
}
class Experiments:
def __init__(self, builder): def __init__(self, builder):
super(Experiments,self).__init__()
self.builder = builder self.builder = builder
self.builder.connect_signals(self)
# (experiment index in UI, experiment) # (experiment index in UI, experiment)
self.classes = {}
self.classes['cae'] = (0, exp.Chronoamp()) classes = {c.id : c() # Make class instances
self.classes['lsv'] = (1, exp.LSV()) for _, c in inspect.getmembers(exp_int, inspect.isclass)
self.classes['cve'] = (2, exp.CV()) if issubclass(c, exp_int.ExpInterface)
self.classes['swv'] = (3, exp.SWV()) and c is not exp_int.ExpInterface
self.classes['dpv'] = (4, exp.DPV()) }
self.classes['acv'] = (5, exp.ACV())
self.classes['pde'] = (6, exp.PD()) self.classes = OrderedDict(sorted(classes.items()))
self.classes['pot'] = (7, exp.POT())
self.classes['cal'] = (8, exp.CAL())
# Create reverse lookup
self.select_to_key = {}
for key, value in self.classes.iteritems():
self.select_to_key[value[0]] = key
# fill exp_section # fill exp_section
exp_section = self.builder.get_object('exp_section_box') exp_section = self.builder.get_object('exp_section_box')
self.containers = {} self.containers = {}
for key, cls in self.classes.iteritems(): for key, c in self.classes.items():
self.containers[key] = cls[1].builder.get_object('scrolledwindow1') c.connect('run_utility', self.on_run_utility)
c.connect('done_utility', self.on_done_utility)
self.containers[key] = c.get_window()
for key in self.containers:
try:
self.containers[key].get_parent().remove(self.containers[key])
except AttributeError:
pass
exp_section.add(self.containers[key])
self.expcombobox = self.builder.get_object('expcombobox')
self.expcombobox.connect('changed', self.on_expcombobox_changed)
for c in self.classes.values():
self.expcombobox.append(id=c.id, text=c.name)
def on_run_utility(self, data=None):
self.emit('run_utility')
def on_done_utility(self, data=None):
self.emit('done_utility')
def on_expcombobox_changed(self, data=None):
"""Change the experiment window when experiment box changed."""
self.set_exp(self.expcombobox.get_active_id())
def setup_exp(self, parameters):
"""Takes parameters.
Returns experiment instance.
"""
exp = self.classes[self.expcombobox.get_active_id()]
try:
exp.param_test(parameters)
except AttributeError:
logger.warning(
"Experiment {} has no defined parameter test.".format(
exp.name)
)
return exp.get_experiment(parameters)
def hide_exps(self):
for key in self.containers: for key in self.containers:
self.containers[key].reparent(exp_section)
self.containers[key].hide() self.containers[key].hide()
def set_exp(self, selection): def set_exp(self, selection):
...@@ -58,16 +113,17 @@ class Experiments: ...@@ -58,16 +113,17 @@ class Experiments:
Arguments: Arguments:
selection -- id string of experiment type selection -- id string of experiment type
""" """
for key in self.containers: self.hide_exps()
self.containers[key].hide()
self.containers[selection].show() self.containers[selection].show()
self.selected_exp = selection self.selected_exp = selection
return True return True
def get_params(self, experiment): def get_params(self, experiment):
return self.classes[experiment][1].params return self.classes[experiment].params
def set_params(self, experiment, parameters): def set_params(self, experiment, parameters):
self.classes[experiment][1].params = parameters try:
\ No newline at end of file self.classes[experiment].params = parameters
except KeyError as e:
logger.warning("Tried to load inavlid experiment with id %s", e.args)
\ No newline at end of file
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from ..dstat import state, dfu
from ..dstat.comm import dstat_logger, exp_logger
import logging
import time
import serial
logger = logging.getLogger(__name__)
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
except ImportError:
print("ERR: GTK not available")
sys.exit(1)
class InfoDialog(object):
def __init__(self, parent, connect, signal='activate'):
self.parent = parent
connect.connect(signal, self.activate)
def activate(self, object=None, data=None):
self.dialog = Gtk.MessageDialog(self.parent, 0, Gtk.MessageType.INFO,
Gtk.ButtonsType.OK, "DStat Info")
self.dialog.format_secondary_text(
"PCB Version: {}\n".format(state.dstat_version.base_version) +
"Firmware Version: {}".format(state.firmware_version)
)
self.dialog.connect('response', self.destroy)
self.dialog.show()
def destroy(self, object=None, data=None):
self.dialog.destroy()
class ResetDialog(object):
def __init__(self, parent, connect, stop_callback, disconnect_callback, signal='activate'):
self.parent = parent
self.stop = stop_callback
self.disconnect = disconnect_callback
connect.connect(signal, self.activate)
def activate(self, object=None, data=None):
dialog = Gtk.MessageDialog(self.parent, 0, Gtk.MessageType.WARNING,
Gtk.ButtonsType.OK_CANCEL, "EEPROM Reset")
dialog.format_secondary_text("This will reset the DStat's EEPROM settings, then disconneect."
)
response = dialog.run()
if response == Gtk.ResponseType.OK:
self.dstat_reset_eeprom()
dialog.destroy()
def dstat_reset_eeprom(self):
"""Tries to contact DStat and resets EEPROM.
If no response, returns False, otherwise True.
"""
self.stop()
exp = EEPROMReset()
state.ser.start_exp(exp)
logger.info("Resetting DStat EEPROM…")
while True:
result = state.ser.get_proc(block=True)
if result in ('SERIAL_ERROR', 'DONE', 'ABORT'):
break
logger.info(result)
self.disconnect()
class EEPROMReset(object):
def __init__(self):
pass
def run(self, ser, ctrl_pipe, data_pipe):
status = None
try:
ser.write(b'!2\n')
exp_logger.info('!2')
for i in range(10):
if ser.readline().rstrip() == b"@ACK 2":
dstat_logger.info('@ACK 2')
ser.write(b'SD\n')
exp_logger.info('SD')
status = "DONE"
time.sleep(5)
break
else:
time.sleep(.5)
ser.reset_input_buffer()
ser.write(b'!2\n')
exp_logger.info('!2')
time.sleep(.1)
except UnboundLocalError as e:
status = "SERIAL_ERROR"
except serial.SerialException as e:
logger.error('SerialException: %s', e)
status = "SERIAL_ERROR"
finally:
return status
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface> <interface>
<requires lib="gtk+" version="2.24"/> <requires lib="gtk+" version="3.10"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="window1"> <object class="GtkWindow" id="window1">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
<object class="GtkScrolledWindow" id="scrolledwindow1"> <object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property> <property name="hexpand">True</property>
<property name="vscrollbar_policy">automatic</property> <property name="vexpand">True</property>
<child> <child>
<object class="GtkViewport" id="viewport1"> <object class="GtkViewport" id="viewport1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="shadow_type">none</property> <property name="shadow_type">none</property>
<child> <child>
<object class="GtkVBox" id="vbox1"> <object class="GtkBox" id="vbox1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkFrame" id="frame1"> <object class="GtkFrame" id="frame1">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -33,11 +34,11 @@ ...@@ -33,11 +34,11 @@
<property name="left_padding">5</property> <property name="left_padding">5</property>
<property name="right_padding">5</property> <property name="right_padding">5</property>
<child> <child>
<object class="GtkTable" id="table1"> <object class="GtkGrid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="n_rows">3</property> <property name="row_homogeneous">True</property>
<property name="n_columns">3</property> <property name="column_homogeneous">True</property>
<child> <child>
<object class="GtkLabel" id="label3"> <object class="GtkLabel" id="label3">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -46,8 +47,7 @@ ...@@ -46,8 +47,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">0</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -58,8 +58,7 @@ ...@@ -58,8 +58,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="right_attach">3</property> <property name="top_attach">0</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -68,7 +67,8 @@ ...@@ -68,7 +67,8 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
</object> </object>
<packing> <packing>
<property name="y_options">GTK_FILL</property> <property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -78,9 +78,8 @@ ...@@ -78,9 +78,8 @@
<property name="label" translatable="yes">Cleaning</property> <property name="label" translatable="yes">Cleaning</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -90,9 +89,8 @@ ...@@ -90,9 +89,8 @@
<property name="label" translatable="yes">Deposition</property> <property name="label" translatable="yes">Deposition</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -103,19 +101,12 @@ ...@@ -103,19 +101,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -126,19 +117,12 @@ ...@@ -126,19 +117,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property> <property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -149,19 +133,12 @@ ...@@ -149,19 +133,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property> <property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -172,19 +149,12 @@ ...@@ -172,19 +149,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
</object> </object>
...@@ -221,13 +191,11 @@ ...@@ -221,13 +191,11 @@
<property name="left_padding">5</property> <property name="left_padding">5</property>
<property name="right_padding">5</property> <property name="right_padding">5</property>
<child> <child>
<object class="GtkTable" id="table2"> <object class="GtkGrid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="n_rows">3</property> <property name="row_homogeneous">True</property>
<property name="n_columns">2</property> <property name="column_homogeneous">True</property>
<property name="column_spacing">10</property>
<property name="homogeneous">True</property>
<child> <child>
<object class="GtkLabel" id="label8"> <object class="GtkLabel" id="label8">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -235,7 +203,8 @@ ...@@ -235,7 +203,8 @@
<property name="label" translatable="yes">Start (mV)</property> <property name="label" translatable="yes">Start (mV)</property>
</object> </object>
<packing> <packing>
<property name="y_options">GTK_FILL</property> <property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -245,9 +214,8 @@ ...@@ -245,9 +214,8 @@
<property name="label" translatable="yes">Stop (mV)</property> <property name="label" translatable="yes">Stop (mV)</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -257,9 +225,8 @@ ...@@ -257,9 +225,8 @@
<property name="label" translatable="yes">Slope (mV/s)</property> <property name="label" translatable="yes">Slope (mV/s)</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -270,18 +237,12 @@ ...@@ -270,18 +237,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">0</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -292,20 +253,12 @@ ...@@ -292,20 +253,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -316,20 +269,12 @@ ...@@ -316,20 +269,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property> <property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
</object> </object>
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface> <interface>
<requires lib="gtk+" version="2.24"/> <requires lib="gtk+" version="3.10"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkAdjustment" id="voltage_adjustment"> <object class="GtkAdjustment" id="voltage_adjustment">
<property name="upper">3000</property> <property name="upper">3000</property>
<property name="step_increment">1</property> <property name="step_increment">1</property>
...@@ -13,24 +13,23 @@ ...@@ -13,24 +13,23 @@
<object class="GtkScrolledWindow" id="scrolledwindow1"> <object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property> <property name="hexpand">True</property>
<property name="vscrollbar_policy">automatic</property> <property name="vexpand">True</property>
<child> <child>
<object class="GtkViewport" id="viewport1"> <object class="GtkViewport" id="viewport1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="shadow_type">none</property> <property name="shadow_type">none</property>
<child> <child>
<object class="GtkVBox" id="vbox1"> <object class="GtkBox" id="vbox1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkTable" id="table1"> <object class="GtkGrid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="n_rows">11</property> <property name="column_homogeneous">True</property>
<property name="n_columns">2</property>
<property name="homogeneous">True</property>
<child> <child>
<object class="GtkEntry" id="time_entry"> <object class="GtkEntry" id="time_entry">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -39,20 +38,12 @@ ...@@ -39,20 +38,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -62,7 +53,8 @@ ...@@ -62,7 +53,8 @@
<property name="label" translatable="yes">Bias Voltage (mV)</property> <property name="label" translatable="yes">Bias Voltage (mV)</property>
</object> </object>
<packing> <packing>
<property name="y_options">GTK_FILL</property> <property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -72,9 +64,8 @@ ...@@ -72,9 +64,8 @@
<property name="label" translatable="yes">Measurement Time (s)</property> <property name="label" translatable="yes">Measurement Time (s)</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -83,10 +74,9 @@ ...@@ -83,10 +74,9 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="max_length">4</property> <property name="max_length">4</property>
<property name="invisible_char"></property> <property name="invisible_char"></property>
<property name="text" translatable="yes">0</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">voltage_adjustment</property> <property name="adjustment">voltage_adjustment</property>
<property name="climb_rate">1</property> <property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property> <property name="snap_to_ticks">True</property>
...@@ -95,7 +85,7 @@ ...@@ -95,7 +85,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -103,10 +93,13 @@ ...@@ -103,10 +93,13 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Ambient Light Interlock</property> <property name="label" translatable="yes">Ambient Light Interlock</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property> <property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -120,10 +113,7 @@ ...@@ -120,10 +113,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property> <property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_EXPAND</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -132,13 +122,12 @@ ...@@ -132,13 +122,12 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="halign">center</property>
<signal name="clicked" handler="on_threshold_button_clicked" swapped="no"/> <signal name="clicked" handler="on_threshold_button_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property> <property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -149,20 +138,12 @@ ...@@ -149,20 +138,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property> <property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -171,24 +152,12 @@ ...@@ -171,24 +152,12 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="halign">center</property>
<signal name="clicked" handler="on_light_button_clicked" swapped="no"/> <signal name="clicked" handler="on_light_button_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property> <property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -204,20 +173,7 @@ ...@@ -204,20 +173,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property> <property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator2">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -225,10 +181,13 @@ ...@@ -225,10 +181,13 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Electromechanical Shutter</property> <property name="label" translatable="yes">Electromechanical Shutter</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property> <property name="top_attach">7</property>
<property name="bottom_attach">8</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -237,13 +196,12 @@ ...@@ -237,13 +196,12 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="yalign">0.54000002145767212</property> <property name="halign">center</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">8</property> <property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="x_options"/>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -254,20 +212,12 @@ ...@@ -254,20 +212,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">8</property> <property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -282,10 +232,7 @@ ...@@ -282,10 +232,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">7</property> <property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="x_options">GTK_EXPAND</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -295,8 +242,8 @@ ...@@ -295,8 +242,8 @@
<property name="label" translatable="yes">Start FFT after (s)</property> <property name="label" translatable="yes">Start FFT after (s)</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">9</property> <property name="top_attach">9</property>
<property name="bottom_attach">10</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -310,16 +257,10 @@ ...@@ -310,16 +257,10 @@
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">9</property> <property name="top_attach">9</property>
<property name="bottom_attach">10</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -329,8 +270,8 @@ ...@@ -329,8 +270,8 @@
<property name="label" translatable="yes">FFT Integral bandwidth (Hz)</property> <property name="label" translatable="yes">FFT Integral bandwidth (Hz)</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">10</property> <property name="top_attach">10</property>
<property name="bottom_attach">11</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -342,25 +283,44 @@ ...@@ -342,25 +283,44 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text">1</property> <property name="text">1</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">10</property> <property name="top_attach">10</property>
<property name="bottom_attach">11</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="left_attach">0</property>
<property name="fill">False</property> <property name="top_attach">2</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
...@@ -368,8 +328,6 @@ ...@@ -368,8 +328,6 @@
<object class="GtkLabel" id="label13"> <object class="GtkLabel" id="label13">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="xpad">20</property>
<property name="ypad">20</property>
<attributes> <attributes>
<attribute name="weight" value="bold"/> <attribute name="weight" value="bold"/>
</attributes> </attributes>
......
...@@ -20,17 +20,20 @@ ...@@ -20,17 +20,20 @@
""" """
Creates data plot. Creates data plot.
""" """
import gtk try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
except ImportError:
print "ERR: GTK not available"
sys.exit(1)
from matplotlib.figure import Figure from matplotlib.figure import Figure
#from matplotlib.backends.backend_gtkcairo\ from matplotlib.backends.backend_gtk3agg \
# import FigureCanvasGTKCairo as FigureCanvas import FigureCanvasGTK3Agg as FigureCanvas
#from matplotlib.backends.backend_gtkcairo\ from matplotlib.backends.backend_gtk3 \
# import NavigationToolbar2Cairo as NavigationToolbar import NavigationToolbar2GTK3 as NavigationToolbar
from matplotlib.backends.backend_gtkagg \
import FigureCanvasGTKAgg as FigureCanvas
from matplotlib.backends.backend_gtkagg \
import NavigationToolbar2GTKAgg as NavigationToolbar
try: try:
import seaborn as sns import seaborn as sns
except ImportError: except ImportError:
...@@ -89,114 +92,3 @@ def findBounds(y): ...@@ -89,114 +92,3 @@ def findBounds(y):
return (start_index, stop_index) return (start_index, stop_index)
class PlotBox(object):
"""Contains main data plot and associated methods."""
def __init__(self, plotwindow_instance):
"""Creates plot and moves it to a gtk container.
Arguments:
plotwindow_instance -- gtk container to hold plot.
"""
self.figure = Figure()
self.figure.subplots_adjust(left=0.07, bottom=0.07,
right=0.96, top=0.96)
self.axe1 = self.figure.add_subplot(111)
self.axe1.plot([0, 1], [0, 1])
self.axe1.ticklabel_format(style='sci', scilimits=(0, 3),
useOffset=False, axis='y')
self.canvas = FigureCanvas(self.figure)
self.win = gtk.Window()
self.vbox = gtk.VBox()
self.win.add(self.vbox)
self.vbox.pack_start(self.canvas)
self.toolbar = NavigationToolbar(self.canvas, self.win)
self.vbox.pack_start(self.toolbar, False, False)
self.vbox.reparent(plotwindow_instance)
def clearall(self):
"""Remove all lines on plot. """
for i in range(len(self.axe1.lines)):
self.axe1.lines.pop(0)
self.addline()
def clearline(self, line_number):
"""Remove a line specified by line_number."""
self.lines[line_number].remove()
self.lines.pop(line_number)
def addline(self):
"""Add a new line to plot. (initialized with dummy data)))"""
self.axe1.plot([0, 1], [0, 1])
def updateline(self, Experiment, line_number):
"""Update a line specified by line_number with data stored in
the Experiment instance.
"""
# limits display to 2000 data points per line
divisor = len(Experiment.data['data'][line_number][0]) // 2000 + 1
self.axe1.lines[line_number].set_ydata(
Experiment.data['data'][line_number][1][1::divisor])
self.axe1.lines[line_number].set_xdata(
Experiment.data['data'][line_number][0][1::divisor])
def changetype(self, Experiment):
"""Change plot type. Set axis labels and x bounds to those stored
in the Experiment instance. Stores class instance in Experiment.
"""
self.axe1.set_xlabel(Experiment.xlabel)
self.axe1.set_ylabel(Experiment.ylabel)
self.axe1.set_xlim(Experiment.xmin, Experiment.xmax)
Experiment.plots['data'] = self
self.figure.canvas.draw()
def redraw(self):
"""Autoscale and refresh the plot."""
self.axe1.relim()
self.axe1.autoscale(True, axis = 'y')
self.figure.canvas.draw()
return True
class FT_Box(PlotBox):
def updateline(self, Experiment, line_number):
def search_value(data, target):
for i in range(len(data)):
if data[i] > target:
return i
y = Experiment.data['data'][line_number][1]
x = Experiment.data['data'][line_number][0]
freq = Experiment.parameters['adc_rate_hz']
i = search_value(x, float(Experiment.parameters['fft_start']))
y1 = y[i:]
x1 = x[i:]
avg = mean(y1)
min_index, max_index = findBounds(y1)
y1[min_index] = avg
y1[max_index] = avg
f, Y = plotSpectrum(y1[min_index:max_index],freq)
self.axe1.lines[line_number].set_ydata(Y)
self.axe1.lines[line_number].set_xdata(f)
Experiment.data['ft'] = [(f, Y)]
def changetype(self, Experiment):
"""Change plot type. Set axis labels and x bounds to those stored
in the Experiment instance. Stores class instance in Experiment.
"""
self.axe1.set_xlabel("Freq (Hz)")
self.axe1.set_ylabel("|Y| (A/Hz)")
self.axe1.set_xlim(0, Experiment.parameters['adc_rate_hz']/2)
Experiment.plots['ft'] = self
self.figure.canvas.draw()
import logging
logger = logging.getLogger(__name__)
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
except ImportError:
print "ERR: GTK not available"
sys.exit(1)
from matplotlib.backends.backend_gtk3 \
import NavigationToolbar2GTK3 as NavigationToolbar
def clear_notebook(notebook):
for pages in range(notebook.get_n_pages()):
notebook.remove_page(0)
def add_exp_to_notebook(notebook, exp):
for plot in exp.plots:
label = Gtk.Label.new(plot.name)
notebook.append_page(plot.box, label)
plot.box.show_all()
def replace_notebook_exp(notebook, exp, window):
clear_notebook(notebook)
add_exp_to_notebook(notebook, exp)
add_navigation_toolbars(exp, window)
def add_navigation_toolbars(exp, window):
for plot in exp.plots:
toolbar = NavigationToolbar(plot.canvas, window)
plot.box.pack_start(toolbar, expand=False, fill=False, padding=0)
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface> <interface>
<requires lib="gtk+" version="2.24"/> <requires lib="gtk+" version="3.10"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkListStore" id="ca_list"> <object class="GtkListStore" id="ca_list">
<columns> <columns>
<!-- column-name millivolts --> <!-- column-name millivolts -->
...@@ -18,23 +18,24 @@ ...@@ -18,23 +18,24 @@
<object class="GtkScrolledWindow" id="scrolledwindow1"> <object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property> <property name="hexpand">True</property>
<property name="vscrollbar_policy">automatic</property> <property name="vexpand">True</property>
<child> <child>
<object class="GtkViewport" id="viewport1"> <object class="GtkViewport" id="viewport1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="shadow_type">none</property> <property name="shadow_type">none</property>
<child> <child>
<object class="GtkVBox" id="vbox1"> <object class="GtkBox" id="vbox1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkTable" id="table1"> <object class="GtkGrid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="n_rows">2</property> <property name="row_homogeneous">True</property>
<property name="n_columns">2</property> <property name="column_homogeneous">True</property>
<child> <child>
<object class="GtkLabel" id="label2"> <object class="GtkLabel" id="label2">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -42,7 +43,8 @@ ...@@ -42,7 +43,8 @@
<property name="label" translatable="yes">Time (s)</property> <property name="label" translatable="yes">Time (s)</property>
</object> </object>
<packing> <packing>
<property name="bottom_attach">2</property> <property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -54,26 +56,19 @@ ...@@ -54,26 +56,19 @@
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="truncate_multiline">True</property> <property name="truncate_multiline">True</property>
<property name="invisible_char_set">True</property>
<property name="caps_lock_warning">False</property> <property name="caps_lock_warning">False</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">0</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_EXPAND</property>
<property name="y_options">GTK_SHRINK</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">False</property>
<property name="padding">5</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
......
...@@ -18,25 +18,33 @@ ...@@ -18,25 +18,33 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division, absolute_import, print_function, unicode_literals
import io import io
import os import os
import gtk
import numpy as np
import logging import logging
logger = logging.getLogger(__name__)
logger = logging.getLogger("dstat.interface.save")
from errors import InputError, VarError try:
from params import save_params, load_params import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
except ImportError:
print("ERR: GTK not available")
sys.exit(1)
import numpy as np
from ..errors import InputError, VarError
from ..params import save_params, load_params
def manSave(current_exp): def manSave(current_exp):
fcd = gtk.FileChooserDialog("Save...", None, gtk.FILE_CHOOSER_ACTION_SAVE, fcd = Gtk.FileChooserDialog("Save", None, Gtk.FileChooserAction.SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK)) Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
filters = [gtk.FileFilter()] filters = [Gtk.FileFilter()]
filters[0].set_name("Space separated text (.txt)") filters[0].set_name("Tab-separated Text (.txt)")
filters[0].add_pattern("*.txt") filters[0].add_pattern("*.txt")
fcd.set_do_overwrite_confirmation(True) fcd.set_do_overwrite_confirmation(True)
...@@ -45,29 +53,29 @@ def manSave(current_exp): ...@@ -45,29 +53,29 @@ def manSave(current_exp):
response = fcd.run() response = fcd.run()
if response == gtk.RESPONSE_OK: if response == Gtk.ResponseType.OK:
path = fcd.get_filename() path = fcd.get_filename().decode("utf-8")
logger.info("Selected filepath: %s", path) logger.info("Selected filepath: %s", path)
filter_selection = fcd.get_filter().get_name() filter_selection = fcd.get_filter().get_name().decode("utf-8")
if filter_selection.endswith("(.txt)"): if filter_selection.endswith("(.txt)"):
save_text(current_exp, path) save_text(current_exp, path)
fcd.destroy() fcd.destroy()
elif response == gtk.RESPONSE_CANCEL: elif response == Gtk.ResponseType.CANCEL:
fcd.destroy() fcd.destroy()
def plot_save_dialog(plots): def plot_save_dialog(plots):
fcd = gtk.FileChooserDialog("Save Plot…", None, fcd = Gtk.FileChooserDialog("Save Plot…", None,
gtk.FILE_CHOOSER_ACTION_SAVE, Gtk.FileChooserAction.SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK)) Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
filters = [gtk.FileFilter()] filters = [Gtk.FileFilter()]
filters[0].set_name("Portable Document Format (.pdf)") filters[0].set_name("Portable Document Format (.pdf)")
filters[0].add_pattern("*.pdf") filters[0].add_pattern("*.pdf")
filters.append(gtk.FileFilter()) filters.append(Gtk.FileFilter())
filters[1].set_name("Portable Network Graphics (.png)") filters[1].set_name("Portable Network Graphics (.png)")
filters[1].add_pattern("*.png") filters[1].add_pattern("*.png")
...@@ -77,10 +85,10 @@ def plot_save_dialog(plots): ...@@ -77,10 +85,10 @@ def plot_save_dialog(plots):
response = fcd.run() response = fcd.run()
if response == gtk.RESPONSE_OK: if response == Gtk.ResponseType.OK:
path = fcd.get_filename() path = fcd.get_filename().decode("utf-8")
logger.info("Selected filepath: %s", path) logger.info("Selected filepath: %r", path)
filter_selection = fcd.get_filter().get_name() filter_selection = fcd.get_filter().get_name().decode("utf-8")
if filter_selection.endswith("(.pdf)"): if filter_selection.endswith("(.pdf)"):
if not path.endswith(".pdf"): if not path.endswith(".pdf"):
...@@ -94,19 +102,18 @@ def plot_save_dialog(plots): ...@@ -94,19 +102,18 @@ def plot_save_dialog(plots):
fcd.destroy() fcd.destroy()
elif response == gtk.RESPONSE_CANCEL: elif response == Gtk.ResponseType.CANCEL:
fcd.destroy() fcd.destroy()
def man_param_save(window): def man_param_save(window):
fcd = gtk.FileChooserDialog("Save Parameters…", fcd = Gtk.FileChooserDialog("Save Parameters…",
None, None,
gtk.FILE_CHOOSER_ACTION_SAVE, Gtk.FileChooserAction.SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK) Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
)
filters = [gtk.FileFilter()] filters = [Gtk.FileFilter()]
filters[0].set_name("Parameter File (.yml)") filters[0].set_name("Parameter File (.yml)")
filters[0].add_pattern("*.yml") filters[0].add_pattern("*.yml")
...@@ -116,8 +123,8 @@ def man_param_save(window): ...@@ -116,8 +123,8 @@ def man_param_save(window):
response = fcd.run() response = fcd.run()
if response == gtk.RESPONSE_OK: if response == Gtk.ResponseType.OK:
path = fcd.get_filename() path = fcd.get_filename().decode("utf-8")
logger.info("Selected filepath: %s", path) logger.info("Selected filepath: %s", path)
if not path.endswith(".yml"): if not path.endswith(".yml"):
...@@ -127,18 +134,17 @@ def man_param_save(window): ...@@ -127,18 +134,17 @@ def man_param_save(window):
fcd.destroy() fcd.destroy()
elif response == gtk.RESPONSE_CANCEL: elif response == Gtk.ResponseType.CANCEL:
fcd.destroy() fcd.destroy()
def man_param_load(window): def man_param_load(window):
fcd = gtk.FileChooserDialog("Load Parameters…", fcd = Gtk.FileChooserDialog("Load Parameters…",
None, None,
gtk.FILE_CHOOSER_ACTION_OPEN, Gtk.FileChooserAction.OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK) Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
)
filters = [gtk.FileFilter()] filters = [Gtk.FileFilter()]
filters[0].set_name("Parameter File (.yml)") filters[0].set_name("Parameter File (.yml)")
filters[0].add_pattern("*.yml") filters[0].add_pattern("*.yml")
...@@ -147,15 +153,15 @@ def man_param_load(window): ...@@ -147,15 +153,15 @@ def man_param_load(window):
response = fcd.run() response = fcd.run()
if response == gtk.RESPONSE_OK: if response == Gtk.ResponseType.OK:
path = fcd.get_filename() path = fcd.get_filename().decode("utf-8")
logger.info("Selected filepath: %s", path) logger.info("Selected filepath: %s", path)
load_params(window, path) load_params(window, path)
fcd.destroy() fcd.destroy()
elif response == gtk.RESPONSE_CANCEL: elif response == Gtk.ResponseType.CANCEL:
fcd.destroy() fcd.destroy()
def autoSave(exp, path, name): def autoSave(exp, path, name):
...@@ -180,62 +186,22 @@ def autoPlot(exp, path, name): ...@@ -180,62 +186,22 @@ def autoPlot(exp, path, name):
save_plot(exp, path) save_plot(exp, path)
def save_text(exp, path): def save_text(exp, path):
name, _sep, ext = path.rpartition('.') # ('','',string) if no match savestrings = exp.get_save_strings()
if _sep == '': path = path.rstrip('.txt')
name = ext
ext = 'txt'
num = '' num = ''
j = 0 j = 0
for dname in exp.data: # Test for any existing files for key, text in savestrings.items(): # Test for existing files of any kind
while os.path.exists("%s%s-%s.%s" % (name, num, dname, ext)): while os.path.exists("{}{}-{}.txt".format(path, num, key)):
j += 1 j += 1
num = j num = j
for dname in exp.data: # save data save_path = "{}{}".format(path, num)
file = open("%s%s-%s.%s" % (name, num, dname, ext), 'w')
time = exp.time
header = "".join(['# TIME ', time.isoformat(), "\n"])
header += "# DSTAT COMMANDS\n# "
for i in exp.commands:
header += i
file.write("".join([header, '\n']))
analysis_buffer = []
if exp.analysis != {}:
analysis_buffer.append("# ANALYSIS")
for key, value in exp.analysis.iteritems():
analysis_buffer.append("# %s:" % key)
for scan in value:
number, result = scan
analysis_buffer.append(
"# Scan %s -- %s" % (number, result)
)
for i in analysis_buffer:
file.write("%s\n" % i)
# Write out actual data
line_buffer = []
for scan in zip(*exp.data[dname]):
for dimension in scan:
for i in range(len(dimension)):
try:
line_buffer[i] += "%s " % dimension[i]
except IndexError:
line_buffer.append("")
line_buffer[i] += "%s " % dimension[i]
for i in line_buffer:
file.write("%s\n" % i)
file.close() for key, text in savestrings.items():
with open('{}-{}.txt'.format(save_path, key), 'w') as f:
f.write(text)
def save_plot(exp, path): def save_plot(exp, path):
"""Saves everything in exp.plots to path. Appends a number for duplicates. """Saves everything in exp.plots to path. Appends a number for duplicates.
...@@ -250,9 +216,11 @@ def save_plot(exp, path): ...@@ -250,9 +216,11 @@ def save_plot(exp, path):
j = 0 j = 0
for i in exp.plots: # Test for any existing files for i in exp.plots: # Test for any existing files
while os.path.exists("%s%s-%s.%s" % (name, num, i, ext)): plot_type = '_'.join(i.name.lower().split())
while os.path.exists("{}{}-{}.{}".format(name, num, plot_type, ext)):
j += 1 j += 1
num = j num = j
for i in exp.plots: # save data for i in exp.plots: # save data
exp.plots[i].figure.savefig("%s%s-%s.%s" % (name, num, i, ext)) plot_type = '_'.join(i.name.lower().split())
\ No newline at end of file i.figure.savefig("{}{}-{}.{}".format(name, num, plot_type, ext))
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface> <interface>
<requires lib="gtk+" version="2.24"/> <requires lib="gtk+" version="3.10"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="window1"> <object class="GtkWindow" id="window1">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
<object class="GtkScrolledWindow" id="scrolledwindow1"> <object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property> <property name="hexpand">True</property>
<property name="vscrollbar_policy">automatic</property> <property name="vexpand">True</property>
<child> <child>
<object class="GtkViewport" id="viewport1"> <object class="GtkViewport" id="viewport1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="shadow_type">none</property> <property name="shadow_type">none</property>
<child> <child>
<object class="GtkVBox" id="vbox1"> <object class="GtkBox" id="vbox1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkFrame" id="frame1"> <object class="GtkFrame" id="frame1">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -33,11 +34,11 @@ ...@@ -33,11 +34,11 @@
<property name="left_padding">5</property> <property name="left_padding">5</property>
<property name="right_padding">5</property> <property name="right_padding">5</property>
<child> <child>
<object class="GtkTable" id="table1"> <object class="GtkGrid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="n_rows">3</property> <property name="row_homogeneous">True</property>
<property name="n_columns">3</property> <property name="column_homogeneous">True</property>
<child> <child>
<object class="GtkLabel" id="label3"> <object class="GtkLabel" id="label3">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -46,8 +47,7 @@ ...@@ -46,8 +47,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">0</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -58,8 +58,7 @@ ...@@ -58,8 +58,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="right_attach">3</property> <property name="top_attach">0</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -68,7 +67,8 @@ ...@@ -68,7 +67,8 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
</object> </object>
<packing> <packing>
<property name="y_options">GTK_FILL</property> <property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -78,9 +78,8 @@ ...@@ -78,9 +78,8 @@
<property name="label" translatable="yes">Cleaning</property> <property name="label" translatable="yes">Cleaning</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -90,9 +89,8 @@ ...@@ -90,9 +89,8 @@
<property name="label" translatable="yes">Deposition</property> <property name="label" translatable="yes">Deposition</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -103,19 +101,12 @@ ...@@ -103,19 +101,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -126,19 +117,12 @@ ...@@ -126,19 +117,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property> <property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -149,19 +133,12 @@ ...@@ -149,19 +133,12 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property> <property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -172,21 +149,17 @@ ...@@ -172,21 +149,17 @@
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child>
<placeholder/>
</child>
</object> </object>
</child> </child>
</object> </object>
...@@ -221,251 +194,198 @@ ...@@ -221,251 +194,198 @@
<property name="left_padding">5</property> <property name="left_padding">5</property>
<property name="right_padding">5</property> <property name="right_padding">5</property>
<child> <child>
<object class="GtkTable" id="table2"> <object class="GtkGrid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="n_rows">7</property> <property name="row_homogeneous">True</property>
<property name="n_columns">2</property> <property name="column_homogeneous">True</property>
<property name="column_spacing">10</property>
<property name="homogeneous">True</property>
<child> <child>
<object class="GtkLabel" id="label8"> <object class="GtkEntry" id="scans_entry">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="label" translatable="yes">Start (mV)</property> <property name="invisible_char"></property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object> </object>
<packing> <packing>
<property name="y_options">GTK_FILL</property> <property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label9"> <object class="GtkCheckButton" id="cyclic_checkbutton">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="label" translatable="yes">Stop (mV)</property> <property name="receives_default">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="use_stock">True</property>
<property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
<property name="top_attach">1</property> <property name="left_attach">1</property>
<property name="bottom_attach">2</property> <property name="top_attach">5</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label10"> <object class="GtkLabel" id="label15">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Step Size (mV)</property> <property name="label" translatable="yes">Scans</property>
</object> </object>
<packing> <packing>
<property name="top_attach">2</property> <property name="left_attach">0</property>
<property name="bottom_attach">3</property> <property name="top_attach">6</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="start_entry"> <object class="GtkLabel" id="label14">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">False</property>
<property name="invisible_char"></property> <property name="tooltip_text" translatable="yes">Scan both forwards and backwards.</property>
<property name="width_chars">8</property> <property name="label" translatable="yes">Cyclic Mode</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">0</property>
<property name="right_attach">2</property> <property name="top_attach">5</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="stop_entry"> <object class="GtkEntry" id="freq_entry">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="invisible_char"></property> <property name="invisible_char"></property>
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="step_entry"> <object class="GtkEntry" id="pulse_entry">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="invisible_char"></property> <property name="invisible_char"></property>
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label11"> <object class="GtkLabel" id="label12">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Pulse Height (mV)</property> <property name="label" translatable="yes">Frequency (Hz)</property>
</object> </object>
<packing> <packing>
<property name="top_attach">3</property> <property name="left_attach">0</property>
<property name="bottom_attach">4</property> <property name="top_attach">4</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label12"> <object class="GtkLabel" id="label11">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Frequency (Hz)</property> <property name="label" translatable="yes">Pulse Height (mV)</property>
</object> </object>
<packing> <packing>
<property name="top_attach">4</property> <property name="left_attach">0</property>
<property name="bottom_attach">5</property> <property name="top_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="pulse_entry"> <object class="GtkEntry" id="step_entry">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="invisible_char"></property> <property name="invisible_char"></property>
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="freq_entry"> <object class="GtkEntry" id="stop_entry">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="invisible_char"></property> <property name="invisible_char"></property>
<property name="width_chars">8</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="top_attach">1</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label14"> <object class="GtkEntry" id="start_entry">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Scan both forwards and backwards.</property> <property name="invisible_char"></property>
<property name="label" translatable="yes">Cyclic Mode</property> <property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object> </object>
<packing> <packing>
<property name="top_attach">5</property> <property name="left_attach">1</property>
<property name="bottom_attach">6</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label15"> <object class="GtkLabel" id="label10">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Scans</property> <property name="label" translatable="yes">Step Size (mV)</property>
</object> </object>
<packing> <packing>
<property name="top_attach">6</property> <property name="left_attach">0</property>
<property name="bottom_attach">7</property> <property name="top_attach">2</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="cyclic_checkbutton"> <object class="GtkLabel" id="label9">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">False</property>
<property name="receives_default">False</property> <property name="label" translatable="yes">Stop (mV)</property>
<property name="use_stock">True</property>
<property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">0</property>
<property name="right_attach">2</property> <property name="top_attach">1</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options"/>
<property name="y_options"/>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="scans_entry"> <object class="GtkLabel" id="label8">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">False</property>
<property name="invisible_char"></property> <property name="label" translatable="yes">Start (mV)</property>
<property name="width_chars">8</property>
<property name="text" translatable="yes">0</property>
<property name="xalign">1</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">0</property>
<property name="right_attach">2</property> <property name="top_attach">0</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options"/>
<property name="y_options">GTK_FILL</property>
<property name="y_padding">3</property>
</packing> </packing>
</child> </child>
</object> </object>
......
...@@ -17,19 +17,20 @@ ...@@ -17,19 +17,20 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import yaml import yaml
from errors import ErrorLogger, InputError from errors import InputError
_logger = ErrorLogger(sender="dstat-interface-params")
logger = logging.getLogger(__name__)
def get_params(window): def get_params(window):
"""Fetches and returns dict of all parameters for saving.""" """Fetches and returns dict of all parameters for saving."""
parameters = {} selection = window.exp_window.expcombobox.get_active_id()
parameters = {'experiment_index' : selection}
selection = window.exp_window.select_to_key[window.expcombobox.get_active()]
parameters['experiment_index'] = selection
try: try:
parameters['version'] = window.version parameters['version'] = window.version
...@@ -45,14 +46,16 @@ def get_params(window): ...@@ -45,14 +46,16 @@ def get_params(window):
return parameters return parameters
def save_params(window, path): def save_params(window, path):
"""Fetches current params and saves to path.""" """Fetches current params and saves to path."""
logger.info("Save to: %s", path)
params = get_params(window) params = get_params(window)
with open(path, 'w') as f: with open(path, 'w') as f:
yaml.dump(params, f) yaml.dump(params, f)
def load_params(window, path): def load_params(window, path):
"""Loads params from a path into UI elements.""" """Loads params from a path into UI elements."""
...@@ -60,20 +63,20 @@ def load_params(window, path): ...@@ -60,20 +63,20 @@ def load_params(window, path):
get_params(window) get_params(window)
except InputError: # Will be thrown because no experiment will be selected except InputError: # Will be thrown because no experiment will be selected
pass pass
except KeyError: # Will be thrown because no experiment will be selected
pass
with open(path, 'r') as f: with open(path, 'r') as f:
params = yaml.load(f) params = yaml.load(f)
set_params(window, params) set_params(window, params)
def set_params(window, params): def set_params(window, params):
window.adc_pot.params = params window.adc_pot.params = params
if 'experiment_index' in params:
if not 'experiment_index' in params: window.exp_window.expcombobox.set_active_id(params['experiment_index'])
logger.warning("Missing experiment parameters.")
return
window.expcombobox.set_active(
window.exp_window.classes[params['experiment_index']][0])
window.exp_window.set_params(params['experiment_index'], params) window.exp_window.set_params(params['experiment_index'], params)
window.analysis_opt_window.params = params window.analysis_opt_window.params = params
window.params_loaded = True window.params_loaded = True
...@@ -98,9 +98,26 @@ class DstatPlugin(ZmqPlugin): ...@@ -98,9 +98,26 @@ class DstatPlugin(ZmqPlugin):
return get_params(self.parent) return get_params(self.parent)
def on_execute__run_active_experiment(self, request): def on_execute__run_active_experiment(self, request):
data = decode_content_data(request)
self.parent.statusbar.push(self.parent.message_context_id, "µDrop " self.parent.statusbar.push(self.parent.message_context_id, "µDrop "
"acquisition requested.") "acquisition requested.")
return self.parent.run_active_experiment() return self.parent.run_active_experiment(
param_override=data.get('params'),
metadata=data.get('metadata')
)
def on_execute__set_metadata(self, request=None):
'''
Args
----
(dict) : Dictionary of metadata to be used in subsequent
experiments. Should include `device_id`, `patient_id`, and
`experiment_id`. Leave blank to reset all metadata fields or set
individual keys to `None` to reset individual values.
'''
data = decode_content_data(request)
self.parent.metadata = request
def on_execute__save_text(self, request): def on_execute__save_text(self, request):
''' '''
......
...@@ -48,9 +48,11 @@ __all__ = ('getVersion') ...@@ -48,9 +48,11 @@ __all__ = ('getVersion')
import re import re
import subprocess import subprocess
import sys import sys
import os.path
import inspect
RELEASE_VERSION_FILE = '{}/RELEASE-VERSION'.format(
RELEASE_VERSION_FILE = 'RELEASE-VERSION' os.path.dirname(os.path.abspath(inspect.stack()[0][1])))
# http://www.python.org/dev/peps/pep-0386/ # http://www.python.org/dev/peps/pep-0386/
_PEP386_SHORT_VERSION_RE = r'\d+(?:\.\d+)+(?:(?:[abc]|rc)\d+(?:\.\d+)*)?' _PEP386_SHORT_VERSION_RE = r'\d+(?:\.\d+)+(?:(?:[abc]|rc)\d+(?:\.\d+)*)?'
......
#!/usr/bin/env python
# DStat Interface - An interface for the open hardware DStat potentiostat
# 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/>.
import serial
from serial.tools import list_ports
import time
import struct
import multiprocessing as mp
import logging
from errors import InputError, VarError
logger = logging.getLogger("dstat.comm")
dstat_logger = logging.getLogger("dstat.comm.DSTAT")
exp_logger = logging.getLogger("dstat.comm.Experiment")
def _serial_process(ser_port, proc_pipe, ctrl_pipe, data_pipe):
ser_logger = logging.getLogger("dstat.comm._serial_process")
ser = delayedSerial(ser_port, baudrate=1000000, timeout=1)
ser_logger.info("Connecting")
ser.write("ck")
ser.flushInput()
ser.write('!')
for i in range(10):
if not ser.read()=="C":
time.sleep(.5)
ser.write('!')
else:
break
while True:
# These can only be called when no experiment is running
if ctrl_pipe.poll():
ctrl_buffer = ctrl_pipe.recv()
if ctrl_buffer == ('a' or "DISCONNECT"):
proc_pipe.send("ABORT")
ser.write('a')
ser_logger.info("ABORT")
if ctrl_buffer == "DISCONNECT":
ser_logger.info("DISCONNECT")
ser.close()
proc_pipe.send("DISCONNECT")
return False
elif proc_pipe.poll():
while ctrl_pipe.poll():
ctrl_pipe.recv()
return_code = proc_pipe.recv().run(ser, ctrl_pipe, data_pipe)
ser_logger.info('Return code: %s', str(return_code))
proc_pipe.send(return_code)
else:
time.sleep(.1)
class SerialConnection(object):
def __init__(self, ser_port):
self.proc_pipe_p, self.proc_pipe_c = mp.Pipe(duplex=True)
self.ctrl_pipe_p, self.ctrl_pipe_c = mp.Pipe(duplex=True)
self.data_pipe_p, self.data_pipe_c = mp.Pipe(duplex=True)
self.proc = mp.Process(target=_serial_process, args=(ser_port,
self.proc_pipe_c, self.ctrl_pipe_c,
self.data_pipe_c))
self.proc.start()
class VersionCheck:
def __init__(self):
pass
def run(self, ser, ctrl_pipe, data_pipe):
"""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
"""
try:
ser.write('V')
for line in ser:
if line.startswith('V'):
input = line.lstrip('V')
elif line.startswith("#"):
dstat_logger.info(line.lstrip().rstrip())
elif line.lstrip().startswith("no"):
dstat_logger.debug(line.lstrip().rstrip())
ser.flushInput()
break
parted = input.rstrip().split('.')
e = "PCB version: "
e += str(input.rstrip())
dstat_logger.info(e)
data_pipe.send((int(parted[0]), int(parted[1])))
status = "DONE"
except UnboundLocalError as e:
status = "SERIAL_ERROR"
except SerialException as e:
logger.error('SerialException: %s', e)
status = "SERIAL_ERROR"
finally:
return status
def version_check(ser_port):
"""Tries to contact DStat and get version. Returns a list of
[(major, minor), serial instance]. If no response, returns empty tuple.
Arguments:
ser_port -- address of serial port to use
"""
try:
global serial_instance
serial_instance = SerialConnection(ser_port)
serial_instance.proc_pipe_p.send(VersionCheck())
result = serial_instance.proc_pipe_p.recv()
if result == "SERIAL_ERROR":
buffer = 1
else:
buffer = serial_instance.data_pipe_p.recv()
logger.debug("version_check done")
return buffer
except:
pass
class Settings:
def __init__(self, task, settings=None):
self.task = task
self.settings = settings
def run(self, ser, ctrl_pipe, data_pipe):
"""Tries to contact DStat and get settings. Returns dict of
settings.
"""
self.ser = ser
if 'w' in self.task:
self.write()
if 'r' in self.task:
data_pipe.send(self.read())
status = "DONE"
return status
def read(self):
settings = {}
self.ser.flushInput()
self.ser.write('!')
while not self.ser.read()=="C":
time.sleep(.5)
self.ser.write('!')
self.ser.write('SR')
for line in self.ser:
if line.lstrip().startswith('S'):
input = line.lstrip().lstrip('S')
elif line.lstrip().startswith("#"):
dstat_logger.info(line.lstrip().rstrip())
elif line.lstrip().startswith("no"):
dstat_logger.debug(line.lstrip().rstrip())
self.ser.flushInput()
break
parted = input.rstrip().split(':')
for i in range(len(parted)):
settings[parted[i].split('.')[0]] = [i, parted[i].split('.')[1]]
return settings
def write(self):
self.ser.flushInput()
self.ser.write('!')
while not self.ser.read()=="C":
time.sleep(.5)
self.ser.write('!')
write_buffer = range(len(self.settings))
for i in self.settings: # make sure settings are in right order
write_buffer[self.settings[i][0]] = self.settings[i][1]
self.ser.write('SW')
for i in write_buffer:
self.ser.write(i)
self.ser.write(' ')
return
def read_settings():
"""Tries to contact DStat and get settings. Returns dict of
settings.
"""
global settings
settings = {}
while serial_instance.data_pipe_p.poll():
serial_instance.data_pipe_p.recv()
serial_instance.proc_pipe_p.send(Settings(task='r'))
settings = serial_instance.data_pipe_p.recv()
logger.debug("read_settings: %s", serial_instance.proc_pipe_p.recv())
return
def write_settings():
"""Tries to write settings to DStat from global settings var.
"""
while serial_instance.data_pipe_p.poll():
serial_instance.data_pipe_p.recv()
serial_instance.proc_pipe_p.send(Settings(task='w', settings=settings))
logger.debug("write_settings: %s", serial_instance.proc_pipe_p.recv())
return
class LightSensor:
def __init__(self):
pass
def run(self, ser, ctrl_pipe, data_pipe):
"""Tries to contact DStat and get light sensor reading. Returns uint of
light sensor clear channel.
"""
ser.flushInput()
ser.write('!')
while not ser.read()=="C":
time.sleep(.5)
ser.write('!')
ser.write('T')
for line in ser:
if line.lstrip().startswith('T'):
input = line.lstrip().lstrip('T')
elif line.lstrip().startswith("#"):
dstat_logger.info(line.lstrip().rstrip())
elif line.lstrip().startswith("no"):
dstat_logger.debug(line.lstrip().rstrip())
ser.flushInput()
break
parted = input.rstrip().split('.')
data_pipe.send(parted[0])
status = "DONE"
return status
def read_light_sensor():
"""Tries to contact DStat and get light sensor reading. Returns uint of
light sensor clear channel.
"""
while serial_instance.data_pipe_p.poll():
serial_instance.data_pipe_p.recv()
serial_instance.proc_pipe_p.send(LightSensor())
logger.info("read_light_sensor: %s", serial_instance.proc_pipe_p.recv())
return serial_instance.data_pipe_p.recv()
class delayedSerial(serial.Serial):
"""Extends Serial.write so that characters are output individually
with a slight delay
"""
def write(self, data):
for i in data:
serial.Serial.write(self, i)
time.sleep(.001)
class SerialDevices(object):
"""Retrieves and stores list of serial devices in self.ports"""
def __init__(self):
try:
self.ports, _, _ = zip(*list_ports.comports())
except ValueError:
self.ports = []
logger.error("No serial ports found")
def refresh(self):
"""Refreshes list of ports."""
self.ports, _, _ = zip(*list_ports.comports())
class Experiment(object):
"""Store and acquire a potentiostat experiment. Meant to be subclassed
to by different experiment types and not used instanced directly.
"""
def __init__(self, parameters):
"""Adds commands for gain and ADC."""
self.parameters = parameters
self.databytes = 8
self.scan = 0
self.time = 0
self.plots = {}
self.data = {}
# list of scans, tuple of dimensions, list of data
self.data['data'] = [([], [])]
self.line_data = ([], [])
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]
self.__gain_trim_table = ['r100_trim', 'r100_trim', 'r3k_trim',
'r30k_trim', 'r300k_trim', 'r3M_trim',
'r30M_trim', 'r100M_trim']
else:
raise VarError(parameters['version'], "Invalid version parameter.")
self.gain = self.__gaintable[int(self.parameters['gain'])]
self.gain_trim = int(
settings[self.__gain_trim_table[int(self.parameters['gain'])]][1])
self.commands = ["EA", "EG"]
if self.parameters['buffer_true']:
self.commands[0] += "2"
else:
self.commands[0] += "0"
self.commands[0] += " "
self.commands[0] += (self.parameters['adc_rate'])
self.commands[0] += " "
self.commands[0] += (self.parameters['adc_pga'])
self.commands[0] += " "
self.commands[1] += (self.parameters['gain'])
self.commands[1] += " "
self.commands[1] += (str(int(self.parameters['short_true'])))
self.commands[1] += " "
def run(self, ser, ctrl_pipe, data_pipe):
"""Execute experiment. Connects and sends handshake signal to DStat
then sends self.commands. Don't call directly as a process in Windows,
use run_wrapper instead.
"""
self.serial = ser
self.ctrl_pipe = ctrl_pipe
self.data_pipe = data_pipe
exp_logger.info("Experiment running")
try:
self.serial.flushInput()
status = "DONE"
for i in self.commands:
logger.info("Command: %s", i)
self.serial.write('!')
while not self.serial.read().startswith("C"):
pass
self.serial.write(i)
if not self.serial_handler():
status = "ABORT"
self.data_postprocessing()
except serial.SerialException:
status = "SERIAL_ERROR"
finally:
while self.ctrl_pipe.poll():
self.ctrl_pipe.recv()
return status
def serial_handler(self):
"""Handles incoming serial transmissions from DStat. Returns False
if stop button pressed and sends abort signal to instrument. Sends
data to self.data_pipe as result of self.data_handler).
"""
scan = 0
try:
while True:
if self.ctrl_pipe.poll():
input = self.ctrl_pipe.recv()
logger.debug("serial_handler: %s", input)
if input == ('a' or "DISCONNECT"):
self.serial.write('a')
logger.info("serial_handler: ABORT pressed!")
return False
for line in self.serial:
if self.ctrl_pipe.poll():
if self.ctrl_pipe.recv() == 'a':
self.serial.write('a')
logger.info("serial_handler: ABORT pressed!")
return False
if line.startswith('B'):
data = self.data_handler(
(scan, self.serial.read(size=self.databytes)))
self.data_pipe.send(data)
elif line.lstrip().startswith('S'):
scan += 1
elif line.lstrip().startswith("#"):
dstat_logger.info(line.lstrip().rstrip())
elif line.lstrip().startswith("no"):
dstat_logger.debug(line.lstrip().rstrip())
self.serial.flushInput()
return True
except serial.SerialException:
return False
def data_handler(self, data_input):
"""Takes data_input as tuple -- (scan, data).
Returns:
(scan number, (voltage, current)) -- voltage in mV, current in A
"""
scan, data = data_input
voltage, current = struct.unpack('<Hl', data) #uint16 + int32
return (scan, (
(voltage-32768)*3000./65536,
(current+self.gain_trim)*(1.5/self.gain/8388607)
)
)
def data_postprocessing(self):
"""No data postprocessing done by default, can be overridden
in subclass.
"""
pass
class CALExp(Experiment):
"""Offset calibration experiment"""
def __init__(self, parameters):
self.parameters = parameters
self.databytes = 8
self.scan = 0
self.data = []
self.commands = ["EA2 3 1 ", "EG", "ER"]
self.commands[1] += str(self.parameters['gain'])
self.commands[1] += " "
self.commands[1] += "0 "
self.commands[2] += "1 32768 "
self.commands[2] += str(self.parameters['time'])
self.commands[2] += " "
self.commands[2] += "0 " # disable photodiode interlock
def serial_handler(self):
"""Handles incoming serial transmissions from DStat. Returns False
if stop button pressed and sends abort signal to instrument. Sends
data to self.data_pipe as result of self.data_handler).
"""
try:
while True:
if self.ctrl_pipe.poll():
input = self.ctrl_pipe.recv()
logger.debug("serial_handler: %s", input)
if input == ('a' or "DISCONNECT"):
self.serial.write('a')
logger.info("serial_handler: ABORT pressed!")
return False
for line in self.serial:
if self.ctrl_pipe.poll():
if self.ctrl_pipe.recv() == 'a':
self.serial.write('a')
logger.info("serial_handler: ABORT pressed!")
return False
if line.startswith('B'):
self.data.append(self.data_handler(
self.serial.read(size=self.databytes)))
elif line.lstrip().startswith("#"):
dstat_logger.info(line.lstrip().rstrip())
elif line.lstrip().startswith("no"):
dstat_logger.debug(line.lstrip().rstrip())
self.serial.flushInput()
return True
except serial.SerialException:
return False
def data_handler(self, data):
"""Takes data_input as tuple -- (scan, data).
Returns:
current
"""
seconds, milliseconds, current = struct.unpack('<HHl', data)
return current
def data_postprocessing(self):
"""Averages data points
"""
sum = 0
self.data[0] = 0 # Skip first point
for i in self.data:
sum += i
sum /= len(self.data)
if (sum > 32767):
sum = 32767
elif (sum < -32768):
sum = -32768
self.data_pipe.send(sum)
class Chronoamp(Experiment):
"""Chronoamperometry experiment"""
def __init__(self, parameters):
super(Chronoamp, self).__init__(parameters)
self.datatype = "linearData"
self.xlabel = "Time (s)"
self.ylabel = "Current (A)"
self.datalength = 2
self.databytes = 8
self.xmin = 0
self.xmax = 0
for i in self.parameters['time']:
self.xmax += int(i)
self.commands += "E"
self.commands[2] += "R"
self.commands[2] += str(len(self.parameters['potential']))
self.commands[2] += " "
for i in self.parameters['potential']:
self.commands[2] += str(int(i*(65536./3000)+32768))
self.commands[2] += " "
for i in self.parameters['time']:
self.commands[2] += str(i)
self.commands[2] += " "
self.commands[2] += "0 " # disable photodiode interlock
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, current = struct.unpack('<HHl', data)
return (scan, (
seconds+milliseconds/1000.,
(current+self.gain_trim)*(1.5/self.gain/8388607)
)
)
class PDExp(Chronoamp):
"""Photodiode/PMT experiment"""
def __init__(self, parameters):
super(Chronoamp, self).__init__(parameters) # Don't want to call CA's init
self.datatype = "linearData"
self.xlabel = "Time (s)"
self.ylabel = "Current (A)"
self.datalength = 2
self.databytes = 8
self.xmin = 0
self.xmax = int(self.parameters['time'])
if self.parameters['shutter_true']:
if self.parameters['sync_true']:
self.commands.append("EZ")
self.commands[-1] += str(self.parameters['sync_freq'])
self.commands[-1] += " "
else:
self.commands.append("E2")
self.commands.append("ER1 ")
if self.parameters['voltage'] == 0: # Special case where V=0
self.commands[-1] += "65535"
else:
self.commands[-1] += str(int(
65535-(self.parameters['voltage']*(65536./3000))))
self.commands[-1] += " "
self.commands[-1] += str(self.parameters['time'])
self.commands[-1] += " "
if self.parameters['interlock_true']:
self.commands[-1] += "1"
else:
self.commands[-1] += "0"
self.commands[-1] += " "
if self.parameters['shutter_true']:
if self.parameters['sync_true']:
self.commands.append("Ez")
else:
self.commands.append("E1")
class PotExp(Experiment):
"""Potentiometry experiment"""
def __init__(self, parameters):
super(PotExp, self).__init__(parameters)
self.datatype = "linearData"
self.xlabel = "Time (s)"
self.ylabel = "Voltage (V)"
self.datalength = 2
self.databytes = 8
self.xmin = 0
self.xmax = self.parameters['time']
self.commands += "E"
self.commands[2] += "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('<HHl', data)
return (scan, (
seconds+milliseconds/1000., voltage*(1.5/8388607.)
)
)
class LSVExp(Experiment):
"""Linear Scan Voltammetry experiment"""
def __init__(self, parameters):
super(LSVExp, self).__init__(parameters)
self.datatype = "linearData"
self.xlabel = "Voltage (mV)"
self.ylabel = "Current (A)"
self.datalength = 2
self.databytes = 6 # uint16 + int32
self.xmin = int(self.parameters['start'])
self.xmax = int(self.parameters['stop'])
self.commands += "E"
self.commands[2] += "L"
self.commands[2] += str(self.parameters['clean_s'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['dep_s'])
self.commands[2] += " "
self.commands[2] += str(int(int(self.parameters['clean_mV'])*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(int(int(self.parameters['dep_mV'])*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(self.parameters['start'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['stop'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['slope'])
self.commands[2] += " "
class CVExp(Experiment):
"""Cyclic Voltammetry experiment"""
def __init__(self, parameters):
super(CVExp, self).__init__(parameters)
self.datatype = "CVData"
self.xlabel = "Voltage (mV)"
self.ylabel = "Current (A)"
self.datalength = 2 * self.parameters['scans'] # x and y for each scan
self.databytes = 6 # uint16 + int32
self.xmin = int(self.parameters['v1'])
self.xmax = int(self.parameters['v2'])
self.commands += "E"
self.commands[2] += "C"
self.commands[2] += str(self.parameters['clean_s'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['dep_s'])
self.commands[2] += " "
self.commands[2] += str(int(int(self.parameters['clean_mV'])*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(int(int(self.parameters['dep_mV'])*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(self.parameters['v1'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['v2'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['start'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['scans'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['slope'])
self.commands[2] += " "
class SWVExp(Experiment):
"""Square Wave Voltammetry experiment"""
def __init__(self, parameters):
super(SWVExp, self).__init__(parameters)
self.datatype = "SWVData"
self.xlabel = "Voltage (mV)"
self.ylabel = "Current (A)"
self.data['data'] = [([], [], [], [])] # voltage, current, forwards, reverse
self.line_data = ([], [], [], [])
self.datalength = 2 * self.parameters['scans']
self.databytes = 10
self.xmin = int(self.parameters['start'])
self.xmax = int(self.parameters['stop'])
self.commands += "E"
self.commands[2] += "S"
self.commands[2] += str(self.parameters['clean_s'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['dep_s'])
self.commands[2] += " "
self.commands[2] += str(int(int(self.parameters['clean_mV'])*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(int(int(self.parameters['dep_mV'])*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(self.parameters['start'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['stop'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['step'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['pulse'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['freq'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['scans'])
self.commands[2] += " "
def data_handler(self, input_data):
"""Overrides Experiment method to calculate difference current"""
scan, data = input_data
# uint16 + int32
voltage, forward, reverse = struct.unpack('<Hll', data)
f_trim = forward+self.gain_trim
r_trim = reverse+self.gain_trim
return (scan, (
(voltage-32768)*3000./65536,
(f_trim-r_trim)*(1.5/self.gain/8388607),
f_trim*(1.5/self.gain/8388607),
r_trim*(1.5/self.gain/8388607)
)
)
class DPVExp(SWVExp):
"""Diffential Pulse Voltammetry experiment."""
def __init__(self, parameters):
"""Overrides SWVExp method, extends Experiment method"""
super(SWVExp, self).__init__(parameters)
self.datatype = "SWVData"
self.xlabel = "Voltage (mV)"
self.ylabel = "Current (A)"
self.data['data'] = [([], [], [], [])] # voltage, current, forwards, reverse
self.line_data = ([], [], [], [])
self.datalength = 2
self.databytes = 10
self.xmin = int(self.parameters['start'])
self.xmax = int(self.parameters['stop'])
self.commands += "E"
self.commands[2] += "D"
self.commands[2] += str(self.parameters['clean_s'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['dep_s'])
self.commands[2] += " "
self.commands[2] += str(int(int(self.parameters['clean_mV'])*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(int(int(self.parameters['dep_mV'])*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(self.parameters['start'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['stop'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['step'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['pulse'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['period'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['width'])
self.commands[2] += " "
class OCPExp(Experiment):
"""Open circuit potential measumement in statusbar."""
def __init__(self):
self.databytes = 8
self.commands = ["EA", "EP"]
self.commands[0] += "2 " # input buffer
self.commands[0] += "3 " # 2.5 Hz sample rate
self.commands[0] += "1 " # 2x PGA
self.commands[1] += "0 " # no timeout
self.commands[1] += "0 " # OCP measurement mode
def data_handler(self, data_input):
"""Overrides Experiment method to only send ADC values."""
scan, data = data_input
# 2*uint16 + int32
seconds, milliseconds, voltage = struct.unpack('<HHl', data)
return (voltage/5.592405e6)
class PMTIdle(Experiment):
"""Open circuit potential measumement in statusbar."""
def __init__(self):
self.databytes = 8
self.commands = ["EA", "EM"]
self.commands[0] += "2 " # input buffer
self.commands[0] += "3 " # 2.5 Hz sample rate
self.commands[0] += "1 " # 2x PGA
def measure_offset(time):
gain_trim_table = [None, 'r100_trim', 'r3k_trim', 'r30k_trim', 'r300k_trim',
'r3M_trim', 'r30M_trim', 'r100M_trim']
parameters = {}
parameters['time'] = time
gain_offset = {}
for i in range(1,8):
parameters['gain'] = i
serial_instance.proc_pipe_p.send(CALExp(parameters))
logger.info("measure_offset: %s", serial_instance.proc_pipe_p.recv())
gain_offset[gain_trim_table[i]] = serial_instance.data_pipe_p.recv()
return gain_offset
\ No newline at end of file
# -*- mode: python -*-
a = Analysis(['interface_test.py'],
hiddenimports=[],
hookspath=None,
runtime_hooks=None)
glade_tree = Tree('./interface', prefix = 'interface', excludes=['*.py','*.pyc'])
drivers_tree = Tree('./drivers', prefix = 'drivers')
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='interface_test.exe',
debug=False,
strip=None,
upx=True,
console=True )
coll = COLLECT(exe,
drivers_tree,
glade_tree,
a.binaries,
a.zipfiles,
a.datas,
strip=None,
upx=True,
name='interface_test')
...@@ -20,88 +20,95 @@ ...@@ -20,88 +20,95 @@
""" GUI Interface for Wheeler Lab DStat """ """ GUI Interface for Wheeler Lab DStat """
from __future__ import division, absolute_import, print_function, unicode_literals
import sys import sys
import os import os
import platform
import multiprocessing import multiprocessing
import uuid import uuid
from copy import deepcopy
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime from datetime import datetime
import logging
from pkg_resources import parse_version
from serial import SerialException
import zmq
from dstat_interface.core.utils.version import getVersion
from dstat_interface.core.experiments import idle, pot
from dstat_interface.core import params, analysis, dstat
from dstat_interface.core.dstat import boards
from dstat_interface.core.interface import (exp_window, adc_pot, plot_ui, data_view,
save, hw_info)
from dstat_interface.core.errors import InputError
from dstat_interface.core.plugin import DstatPlugin, get_hub_uri
try: try:
import pygtk import gi
pygtk.require('2.0') gi.require_version('Gtk', '3.0')
except ImportError: from gi.repository import Gtk, GObject
print "ERR: PyGTK 2.0 not available"
sys.exit(1)
try:
import gtk
except ImportError:
print "ERR: GTK not available"
sys.exit(1)
try:
import gobject
except ImportError: except ImportError:
print "ERR: gobject not available" print("ERR: GTK not available")
sys.exit(1) sys.exit(1)
from serial import SerialException
import logging
os.chdir(os.path.dirname(os.path.abspath(sys.argv[0])))
from version import getVersion
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 params
import parameter_test
import analysis
import zmq
from errors import InputError
from plugin import DstatPlugin, get_hub_uri mod_dir = os.path.dirname(os.path.abspath(__file__))
conf_path = os.path.join(os.path.expanduser("~"), '.dstat-interface')
# if __name__ == "__parents_main__": # Only runs for forking emulation on win
# sys.path.append(mod_dir)
# Setup Logging # Setup Logging
root_logger = logging.getLogger("dstat") logger = logging.getLogger(__name__)
root_logger.setLevel(level=logging.INFO) core_logger = logging.getLogger("dstat_interface.core")
loggers = [logger, core_logger]
log_handler = logging.StreamHandler() log_handler = logging.StreamHandler()
log_formatter = logging.Formatter( log_formatter = logging.Formatter(
fmt='%(asctime)s [%(name)s](%(levelname)s) %(message)s', fmt='%(asctime)s %(levelname)s: [%(name)s] %(message)s',
datefmt='%H:%M:%S' datefmt='%H:%M:%S'
) )
log_handler.setFormatter(log_formatter) log_handler.setFormatter(log_formatter)
root_logger.addHandler(log_handler)
logger = logging.getLogger("dstat.main") for log in loggers:
log.setLevel(level=logging.INFO)
log.addHandler(log_handler)
class Main(object):
class Main(GObject.Object):
"""Main program """ """Main program """
__gsignals__ = {
b'exp_start': (GObject.SIGNAL_RUN_FIRST, None, ()),
b'exp_stop': (GObject.SIGNAL_RUN_FIRST, None, ())
}
def __init__(self): def __init__(self):
self.builder = gtk.Builder() super(Main, self).__init__()
self.builder.add_from_file('interface/dstatinterface.glade') self.builder = Gtk.Builder()
self.builder.add_from_file(
os.path.join(mod_dir, 'core/interface/dstatinterface.glade'))
self.builder.connect_signals(self) self.builder.connect_signals(self)
self.cell = gtk.CellRendererText() self.cell = Gtk.CellRendererText()
# Create instance of interface components # Create instance of interface components
self.statusbar = self.builder.get_object('statusbar') self.statusbar = self.builder.get_object('statusbar')
self.ocp_disp = self.builder.get_object('ocp_disp') self.ocp_disp = self.builder.get_object('ocp_disp')
self.window = self.builder.get_object('window1') self.window = self.builder.get_object('window1')
self.aboutdialog = self.builder.get_object('aboutdialog1') self.aboutdialog = self.builder.get_object('aboutdialog1')
self.rawbuffer = self.builder.get_object('databuffer1')
self.databuffer = self.builder.get_object('databuffer2')
self.stopbutton = self.builder.get_object('pot_stop') self.stopbutton = self.builder.get_object('pot_stop')
self.startbutton = self.builder.get_object('pot_start') self.startbutton = self.builder.get_object('pot_start')
self.startbutton.set_sensitive(False)
self.exp_progressbar = self.builder.get_object('exp_progressbar')
self.adc_pot = adc_pot.adc_pot() self.adc_pot = adc_pot.adc_pot()
self.error_context_id = self.statusbar.get_context_id("error") self.error_context_id = self.statusbar.get_context_id("error")
self.message_context_id = self.statusbar.get_context_id("message") self.message_context_id = self.statusbar.get_context_id("message")
self.plotwindow = self.builder.get_object('plotbox')
self.ft_window = self.builder.get_object('ft_box')
self.period_window = self.builder.get_object('period_box')
self.exp_window = exp_window.Experiments(self.builder) self.exp_window = exp_window.Experiments(self.builder)
self.exp_window.connect('run_utility', self.stop_ocp)
self.exp_window.connect('done_utility', self.start_ocp)
self.analysis_opt_window = analysis.AnalysisOptions(self.builder) self.analysis_opt_window = analysis.AnalysisOptions(self.builder)
# Setup Autosave # Setup Autosave
...@@ -111,9 +118,9 @@ class Main(object): ...@@ -111,9 +118,9 @@ class Main(object):
# Setup Plots # Setup Plots
self.plot_notebook = self.builder.get_object('plot_notebook') self.plot_notebook = self.builder.get_object('plot_notebook')
self.main_notebook = self.builder.get_object('main_notebook')
self.plot = plot.PlotBox(self.plotwindow) self.data_view = data_view.DataPage(self.main_notebook)
self.ft_plot = plot.FT_Box(self.ft_window) self.info_page = data_view.InfoPage(self.main_notebook)
# fill adc_pot_box # fill adc_pot_box
self.adc_pot_box = self.builder.get_object('gain_adc_box') self.adc_pot_box = self.builder.get_object('gain_adc_box')
...@@ -128,25 +135,41 @@ class Main(object): ...@@ -128,25 +135,41 @@ class Main(object):
self.serial_combobox = self.builder.get_object('serial_combobox') self.serial_combobox = self.builder.get_object('serial_combobox')
self.serial_combobox.pack_start(self.cell, True) self.serial_combobox.pack_start(self.cell, True)
self.serial_combobox.add_attribute(self.cell, 'text', 0) self.serial_combobox.add_attribute(self.cell, 'text', 0)
self.serial_liststore = self.builder.get_object('serial_liststore') self.serial_liststore = self.builder.get_object('serial_liststore')
self.serial_devices = comm.SerialDevices() self.serial_devices = dstat.comm.SerialDevices()
for i in self.serial_devices.ports: for i in self.serial_devices.ports:
self.serial_liststore.append([i]) self.serial_liststore.append([i])
self.serial_combobox.set_active(0) self.serial_combobox.set_active(0)
#initialize experiment selection combobox
self.expcombobox = self.builder.get_object('expcombobox')
self.expcombobox.pack_start(self.cell, True)
self.expcombobox.add_attribute(self.cell, 'text', 2)
self.expcombobox.set_active(0)
self.spinner = self.builder.get_object('spinner') self.spinner = self.builder.get_object('spinner')
self.mainwindow = self.builder.get_object('window1') self.mainwindow = self.builder.get_object('window1')
self.menu_dstat_info = self.builder.get_object('menu_dstat_info')
self.menu_dstat_info.set_sensitive(False)
self.dstat_info_window = hw_info.InfoDialog(
parent=self.mainwindow,
connect=self.menu_dstat_info
)
self.menu_dstat_fw = self.builder.get_object('menu_dstat_fw')
self.menu_dstat_fw.set_sensitive(False)
self.dstat_fw_window = dstat.dfu.FWDialog(
parent=self.mainwindow,
connect=self.menu_dstat_fw,
stop_callback=self.stop_ocp,
disconnect_callback=self.on_serial_disconnect_clicked
)
self.menu_dstat_reset = self.builder.get_object('menu_dstat_reset')
self.menu_dstat_reset.set_sensitive(False)
self.menu_dstat_reset_window = hw_info.ResetDialog(
parent=self.mainwindow,
connect=self.menu_dstat_reset,
stop_callback=self.stop_ocp,
disconnect_callback=self.on_serial_disconnect_clicked
)
# Set Version Strings # Set Version Strings
try: try:
ver = getVersion() ver = getVersion()
...@@ -157,8 +180,7 @@ class Main(object): ...@@ -157,8 +180,7 @@ class Main(object):
self.aboutdialog.set_version(ver) self.aboutdialog.set_version(ver)
self.mainwindow.show_all() self.mainwindow.show_all()
self.exp_window.hide_exps()
self.on_expcombobox_changed()
self.expnumber = 0 self.expnumber = 0
...@@ -172,10 +194,7 @@ class Main(object): ...@@ -172,10 +194,7 @@ class Main(object):
self.dropbot_enabled = False self.dropbot_enabled = False
self.dropbot_triggered = False self.dropbot_triggered = False
self.plot_notebook.get_nth_page( self.metadata = None # Should only be added to by plugin interface
self.plot_notebook.page_num(self.ft_window)).hide()
self.plot_notebook.get_nth_page(
self.plot_notebook.page_num(self.period_window)).hide()
self.params_loaded = False self.params_loaded = False
# Disable 0MQ plugin API by default. # Disable 0MQ plugin API by default.
...@@ -196,77 +215,100 @@ class Main(object): ...@@ -196,77 +215,100 @@ class Main(object):
def quit(self): def quit(self):
"""Disconnect and save parameters on quit.""" """Disconnect and save parameters on quit."""
params.save_params(self, 'last_params.yml')
try:
params.save_params(self, os.path.join(conf_path, 'last_params.yml'))
self.on_serial_disconnect_clicked() self.on_serial_disconnect_clicked()
gtk.main_quit() except KeyError:
pass
mainloop.quit()
def on_gtk_about_activate(self, menuitem, data=None): def on_gtk_about_activate(self, menuitem, data=None):
"""Display the about window.""" """Display the about window."""
self.response = self.aboutdialog.run() # waits for user to click close self.aboutdialog.run() # waits for user to click close
self.aboutdialog.hide() self.aboutdialog.hide()
def on_menu_analysis_options_activate(self, menuitem, data=None): def on_menu_analysis_options_activate(self, menuitem, data=None):
self.analysis_opt_window.show() self.analysis_opt_window.show()
def on_expcombobox_changed(self, data=None):
"""Change the experiment window when experiment box changed."""
model = self.expcombobox.get_model()
_, id, _ = model[self.expcombobox.get_active()] # id is in 2nd col
self.statusbar.remove_all(self.error_context_id)
if not self.exp_window.set_exp(id):
self.statusbar.push(
self.error_context_id, "Experiment not yet implemented")
def on_serial_refresh_clicked(self, data=None): def on_serial_refresh_clicked(self, data=None):
"""Refresh list of serial devices.""" """Refresh list of serial devices."""
try:
self.serial_devices.refresh() self.serial_devices.refresh()
self.serial_liststore.clear() self.serial_liststore.clear()
except ValueError:
logger.warning("No DStats found")
for i in self.serial_devices.ports: for i in self.serial_devices.ports:
self.serial_liststore.append([i]) self.serial_liststore.append([i])
def on_serial_connect_clicked(self, data=None): def on_serial_connect_clicked(self, widget):
"""Connect and retrieve DStat version.""" """Connect and retrieve DStat version."""
selection = self.serial_combobox.get_active_iter()
if selection is None:
return
if widget is self.serial_pmt_connect:
self.pmt_mode = True
self.adc_pot.ui['short_true'].set_active(True)
self.adc_pot.ui['short_true'].set_sensitive(False)
try: try:
self.serial_connect.set_sensitive(False) self.serial_connect.set_sensitive(False)
self.version = comm.version_check(self.serial_liststore.get_value( self.serial_pmt_connect.set_sensitive(False)
self.serial_combobox.get_active_iter(), 0)) dstat.comm.version_check(
self.serial_liststore.get_value(selection, 0)
)
self.statusbar.remove_all(self.error_context_id) dstat.state.board_instance = boards.find_board(
dstat.state.dstat_version)()
if not len(self.version) == 2: self.statusbar.remove_all(self.error_context_id)
self.statusbar.push(self.error_context_id,
"Communication Error")
return
else: self.adc_pot.set_version()
self.adc_pot.set_version(self.version) self.statusbar.push(
self.statusbar.push(self.error_context_id, self.message_context_id,
"".join(["DStat version: ", "DStat version: {}".format(
str(self.version[0]), dstat.state.dstat_version.base_version)
".", str(self.version[1])])
) )
comm.read_settings() dstat.comm.read_settings()
try:
if dstat.state.settings['dac_units_true'][1] != b'1':
dstat.state.settings['dac_units_true'][1] = b'1'
dstat.comm.write_settings()
except KeyError:
logger.warning("Connected DStat does not support sending DAC units.")
dialog = Gtk.MessageDialog(
self.window, 0, Gtk.MessageType.WARNING,
Gtk.ButtonsType.OK, "Connected DStat does not support sending DAC units." +
"Update firmware or set potentials will be incorrect!")
dialog.run()
dialog.destroy()
self.start_ocp() self.start_ocp()
self.connected = True self.connected = True
self.serial_connect.set_sensitive(False)
self.serial_pmt_connect.set_sensitive(False)
self.serial_disconnect.set_sensitive(True) self.serial_disconnect.set_sensitive(True)
except AttributeError as err: except:
logger.warning("AttributeError: %s", err)
self.serial_connect.set_sensitive(True)
except TypeError as err:
logger.warning("TypeError: %s", err)
self.serial_connect.set_sensitive(True) self.serial_connect.set_sensitive(True)
self.serial_pmt_connect.set_sensitive(True)
self.adc_pot.ui['short_true'].set_sensitive(True)
raise
if self.params_loaded == False: if self.params_loaded == False:
try: try:
params.load_params(self, 'last_params.yml') try:
os.makedirs(conf_path)
except OSError:
if not os.path.isdir(conf_path):
raise
params.load_params(self,
os.path.join(conf_path, 'last_params.yml')
)
except IOError: except IOError:
logger.info("No previous parameters found.") logger.info("No previous parameters found.")
...@@ -280,72 +322,92 @@ class Main(object): ...@@ -280,72 +322,92 @@ class Main(object):
self.stop_ocp() self.stop_ocp()
else: else:
self.on_pot_stop_clicked() self.on_pot_stop_clicked()
comm.serial_instance.ctrl_pipe_p.send("DISCONNECT") dstat.state.ser.disconnect()
comm.serial_instance.proc.terminate()
except AttributeError as err: except AttributeError as err:
logger.warning("AttributeError: %s", err) logger.warning("AttributeError: %s", err)
pass pass
if self.pmt_mode is True:
self.adc_pot.ui['short_true'].set_sensitive(True)
self.pmt_mode = False self.pmt_mode = False
self.connected = False self.connected = False
self.serial_connect.set_sensitive(True) self.serial_connect.set_sensitive(True)
self.serial_pmt_connect.set_sensitive(True) self.serial_pmt_connect.set_sensitive(True)
self.serial_disconnect.set_sensitive(False) self.serial_disconnect.set_sensitive(False)
self.startbutton.set_sensitive(False)
self.stopbutton.set_sensitive(False)
self.menu_dstat_info.set_sensitive(False)
self.menu_dstat_fw.set_sensitive(False)
self.menu_dstat_reset.set_sensitive(False)
self.adc_pot.ui['short_true'].set_sensitive(True) self.adc_pot.ui['short_true'].set_sensitive(True)
def on_pmt_mode_clicked(self, data=None): dstat.state.reset()
"""Connect in PMT mode"""
self.pmt_mode = True
self.adc_pot.ui['short_true'].set_active(True)
self.adc_pot.ui['short_true'].set_sensitive(False)
self.on_serial_connect_clicked()
def start_ocp(self): def start_ocp(self, data=None):
"""Start OCP measurements.""" """Start OCP measurements."""
if dstat.state.dstat_version >= parse_version('1.2'):
if self.version[0] >= 1 and self.version[1] >= 2:
# Flush data pipe # Flush data pipe
while comm.serial_instance.data_pipe_p.poll(): dstat.state.ser.flush_data()
comm.serial_instance.data_pipe_p.recv()
if self.pmt_mode == True: if self.pmt_mode is True:
logger.info("Start PMT idle mode") logger.info("Start PMT idle mode")
comm.serial_instance.proc_pipe_p.send(comm.PMTIdle()) dstat.state.ser.start_exp(idle.PMTIdle())
self.ocp_is_running = True
self.ocp_proc = (GObject.timeout_add(250, self.ocp_running_proc)
,
)
else: else:
logger.info("Start OCP") logger.info("Start OCP")
comm.serial_instance.proc_pipe_p.send(comm.OCPExp()) dstat.state.ser.start_exp(idle.OCPExp())
self.ocp_proc = (gobject.timeout_add(300, self.ocp_running_data), self.ocp_proc = (GObject.timeout_add(300, self.ocp_running_data),
gobject.timeout_add(250, self.ocp_running_proc) GObject.timeout_add(250, self.ocp_running_proc)
) )
self.ocp_is_running = True self.ocp_is_running = False
GObject.timeout_add(100, self.ocp_assert) # Check if getting data
else: else:
logger.info("OCP measurements not supported on v1.1 boards.") logger.info("OCP measurements not supported on v1.1 boards.")
return return
def stop_ocp(self): def stop_ocp(self, data=None):
"""Stop OCP measurements.""" """Stop OCP measurements."""
if self.version[0] >= 1 and self.version[1] >= 2: if dstat.state.dstat_version >= parse_version('1.2'):
if self.pmt_mode == True: if self.pmt_mode == True:
logger.info("Stop PMT idle mode") logger.info("Stop PMT idle mode")
else: else:
logger.info("Stop OCP") logger.info("Stop OCP")
comm.serial_instance.ctrl_pipe_p.send('a') dstat.state.ser.send_ctrl('a')
for i in self.ocp_proc: for i in self.ocp_proc:
gobject.source_remove(i) GObject.source_remove(i)
while self.ocp_running_proc(): while self.ocp_running_proc():
pass pass
self.ocp_is_running = False
self.ocp_disp.set_text("") self.ocp_disp.set_text("")
self.ocp_is_running = False
self.startbutton.set_sensitive(True)
self.menu_dstat_info.set_sensitive(True)
self.menu_dstat_fw.set_sensitive(True)
self.menu_dstat_reset.set_sensitive(True)
else: else:
logger.error("OCP measurements not supported on v1.1 boards.") logger.error("OCP measurements not supported on v1.1 boards.")
return return
def ocp_assert(self):
if self.ocp_is_running:
self.startbutton.set_sensitive(True)
self.menu_dstat_info.set_sensitive(True)
self.menu_dstat_fw.set_sensitive(True)
self.menu_dstat_reset.set_sensitive(True)
return False
else:
return True
def ocp_running_data(self): def ocp_running_data(self):
"""Receive OCP value from experiment process and update ocp_disp field """Receive OCP value from experiment process and update ocp_disp field
...@@ -356,21 +418,22 @@ class Main(object): ...@@ -356,21 +418,22 @@ class Main(object):
""" """
try: try:
if comm.serial_instance.data_pipe_p.poll(): incoming = dstat.state.ser.get_data()
incoming = comm.serial_instance.data_pipe_p.recv() while incoming is not None:
if isinstance(incoming, basestring): # test if incoming is str if isinstance(incoming, basestring): # test if incoming is str
self.on_serial_disconnect_clicked() self.on_serial_disconnect_clicked()
return False return False
try:
data = "".join(["OCP: ", data = "".join(["OCP: ",
"{0:.3f}".format(incoming), "{0:.3f}".format(incoming),
" V"]) " V"])
self.ocp_disp.set_text(data) self.ocp_disp.set_text(data)
self.ocp_is_running = True
except ValueError:
pass
if comm.serial_instance.data_pipe_p.poll(): incoming = dstat.state.ser.get_data()
self.ocp_running_data()
return True
return True return True
...@@ -389,19 +452,16 @@ class Main(object): ...@@ -389,19 +452,16 @@ class Main(object):
""" """
try: try:
if comm.serial_instance.proc_pipe_p.poll(): proc_buffer = dstat.state.ser.get_proc()
proc_buffer = comm.serial_instance.proc_pipe_p.recv() while proc_buffer is not None:
logger.debug("ocp_running_proc: %s", proc_buffer) logger.debug("ocp_running_proc: %s", proc_buffer)
if proc_buffer in ["DONE", "SERIAL_ERROR", "ABORT"]: if proc_buffer in ["DONE", "SERIAL_ERROR", "ABORT"]:
if proc_buffer == "SERIAL_ERROR": if proc_buffer == "SERIAL_ERROR":
self.on_serial_disconnect_clicked() self.on_serial_disconnect_clicked()
while comm.serial_instance.data_pipe_p.poll(): dstat.state.ser.flush_data()
comm.serial_instance.data_pipe_p.recv()
return False return False
proc_buffer = dstat.state.ser.get_proc()
return True
return True return True
except EOFError: except EOFError:
...@@ -417,11 +477,17 @@ class Main(object): ...@@ -417,11 +477,17 @@ class Main(object):
# Ignore expected exceptions when triggering experiment from UI. # Ignore expected exceptions when triggering experiment from UI.
pass pass
def run_active_experiment(self): def run_active_experiment(self, param_override=None, metadata=None):
"""Run currently visible experiment.""" """Run currently visible experiment."""
# Assign current experiment a unique identifier. # Assign current experiment a unique identifier.
experiment_id = uuid.uuid4() experiment_id = uuid.uuid4()
self.active_experiment_id = experiment_id self.active_experiment_id = experiment_id
logger.info("Current measurement id: %s", experiment_id.hex)
self.metadata = metadata
if self.metadata is not None:
logger.info("Loading external metadata")
def exceptions(): def exceptions():
""" Cleans up after errors """ """ Cleans up after errors """
...@@ -430,53 +496,22 @@ class Main(object): ...@@ -430,53 +496,22 @@ class Main(object):
self.stopbutton.set_sensitive(False) self.stopbutton.set_sensitive(False)
self.start_ocp() self.start_ocp()
def run_experiment():
""" Starts experiment """
self.plot.clearall()
self.plot.changetype(self.current_exp)
nb = self.plot_notebook
if (parameters['sync_true'] and parameters['shutter_true']):
nb.get_nth_page(
nb.page_num(self.ft_window)).show()
# nb.get_nth_page(
# nb.page_num(self.period_window)).show()
self.ft_plot.clearall()
self.ft_plot.changetype(self.current_exp)
else:
nb.get_nth_page(nb.page_num(self.ft_window)).hide()
# nb.get_nth_page(nb.page_num(self.period_window)).hide()
comm.serial_instance.proc_pipe_p.send(self.current_exp)
# Flush data pipe
while comm.serial_instance.data_pipe_p.poll():
comm.serial_instance.data_pipe_p.recv()
self.plot_proc = gobject.timeout_add(200,
self.experiment_running_plot)
self.experiment_proc = (
gobject.idle_add(self.experiment_running_data),
gobject.idle_add(self.experiment_running_proc)
)
self.stop_ocp() self.stop_ocp()
self.statusbar.remove_all(self.error_context_id) self.statusbar.remove_all(self.error_context_id)
while comm.serial_instance.data_pipe_p.poll(): # Clear data pipe dstat.state.ser.flush_data()
comm.serial_instance.data_pipe_p.recv()
selection = self.expcombobox.get_active()
parameters = {} parameters = {}
parameters['version'] = self.version parameters['metadata'] = self.metadata
# Make sure these are defined # Make sure these are defined
parameters['sync_true'] = False parameters['sync_true'] = False
parameters['shutter_true'] = False parameters['shutter_true'] = False
try: try:
parameters.update(self.adc_pot.params) if param_override is not None:
parameters.update(self.analysis_opt_window.params) params.set_params(self, param_override)
parameters.update(params.get_params(self))
self.line = 0 self.line = 0
self.lastline = 0 self.lastline = 0
...@@ -487,90 +522,30 @@ class Main(object): ...@@ -487,90 +522,30 @@ class Main(object):
self.stopbutton.set_sensitive(True) self.stopbutton.set_sensitive(True)
self.statusbar.remove_all(self.error_context_id) self.statusbar.remove_all(self.error_context_id)
if selection == 0: # CA try:
# Add experiment parameters to existing del self.current_exp
parameters.update(self.exp_window.get_params('cae')) except AttributeError:
if not parameters['potential']: pass
raise InputError(parameters['potential'],
"Step table is empty")
self.current_exp = comm.Chronoamp(parameters)
self.rawbuffer.set_text("")
self.rawbuffer.place_cursor(self.rawbuffer.get_start_iter())
for i in self.current_exp.commands:
self.rawbuffer.insert_at_cursor(i)
run_experiment()
return experiment_id
elif selection == 1: # LSV
parameters.update(self.exp_window.get_params('lsv'))
parameter_test.lsv_test(parameters)
self.current_exp = comm.LSVExp(parameters)
run_experiment()
return experiment_id
elif selection == 2: # CV
parameters.update(self.exp_window.get_params('cve'))
parameter_test.cv_test(parameters)
self.current_exp = comm.CVExp(parameters)
run_experiment()
return experiment_id
elif selection == 3: # SWV
parameters.update(self.exp_window.get_params('swv'))
parameter_test.swv_test(parameters)
self.current_exp = comm.SWVExp(parameters)
run_experiment()
return experiment_id
elif selection == 4: # DPV
parameters.update(self.exp_window.get_params('dpv'))
parameter_test.dpv_test(parameters)
self.current_exp = comm.DPVExp(parameters)
run_experiment()
return experiment_id
elif selection == 6: # PD
parameters.update(self.exp_window.get_params('pde'))
parameter_test.pd_test(parameters)
self.current_exp = comm.PDExp(parameters) callbacks = {'experiment_done' : self.experiment_done,
run_experiment() 'progress_update' : self.progress_update}
return experiment_id self.current_exp = self.exp_window.setup_exp(parameters)
elif selection == 7: # POT plot_ui.replace_notebook_exp(
if not (self.version[0] >= 1 and self.version[1] >= 2): self.plot_notebook, self.current_exp, self.window
self.statusbar.push(self.error_context_id, )
"v1.1 board does not support potentiometry.")
exceptions()
return
parameters.update(self.exp_window.get_params('pot')) self.data_view.clear_exps()
parameter_test.pot_test(parameters) self.info_page.clear()
self.current_exp = comm.PotExp(parameters) dstat.state.ser.start_exp(self.current_exp)
run_experiment()
# Flush data pipe
dstat.state.ser.flush_data()
self.current_exp.setup_loops(callbacks)
return experiment_id return experiment_id
else:
self.statusbar.push(self.error_context_id,
"Experiment not yet implemented.")
exceptions()
except ValueError as i: except ValueError as i:
logger.info("ValueError: %s",i) logger.info("ValueError: %s",i)
self.statusbar.push(self.error_context_id, self.statusbar.push(self.error_context_id,
...@@ -579,6 +554,8 @@ class Main(object): ...@@ -579,6 +554,8 @@ class Main(object):
raise raise
except KeyError as i: except KeyError as i:
import traceback
traceback.print_exc()
logger.info("KeyError: %s", i) logger.info("KeyError: %s", i)
self.statusbar.push(self.error_context_id, self.statusbar.push(self.error_context_id,
"Experiment parameters must be integers.") "Experiment parameters must be integers.")
...@@ -604,6 +581,12 @@ class Main(object): ...@@ -604,6 +581,12 @@ class Main(object):
exceptions() exceptions()
raise raise
def progress_update(self, widget, progress):
if 0 <= progress <= 1:
self.exp_progressbar.set_fraction(progress)
else:
self.exp_progressbar.pulse()
def experiment_running_data(self): def experiment_running_data(self):
"""Receive data from experiment process and add to """Receive data from experiment process and add to
current_exp.data['data]. current_exp.data['data].
...@@ -615,30 +598,35 @@ class Main(object): ...@@ -615,30 +598,35 @@ class Main(object):
function from GTK's queue. function from GTK's queue.
""" """
try: try:
if comm.serial_instance.data_pipe_p.poll(): incoming = dstat.state.ser.get_data()
incoming = comm.serial_instance.data_pipe_p.recv() while incoming is not None:
try:
self.line, data = incoming self.line, data = incoming
if self.line > self.lastdataline: if self.line > self.lastdataline:
self.current_exp.data['data'].append( newline = True
deepcopy(self.current_exp.line_data)) try:
logger.info("running scan_process()")
self.current_exp.scan_process(self.lastdataline)
except AttributeError:
pass
self.lastdataline = self.line self.lastdataline = self.line
else:
newline = False
self.current_exp.store_data(incoming, newline)
for i in range(len(self.current_exp.data['data'][self.line])): if newline:
self.current_exp.data['data'][self.line][i].append(data[i]) self.experiment_running_plot()
except TypeError:
if comm.serial_instance.data_pipe_p.poll(): pass
self.experiment_running_data() incoming = dstat.state.ser.get_data()
return True
return True return True
except EOFError as err: except EOFError as err:
print err logger.error(err)
self.experiment_done() self.experiment_done()
return False return False
except IOError as err: except IOError as err:
print err logger.error(err)
self.experiment_done() self.experiment_done()
return False return False
...@@ -652,9 +640,15 @@ class Main(object): ...@@ -652,9 +640,15 @@ class Main(object):
function from GTK's queue. function from GTK's queue.
""" """
try: try:
if comm.serial_instance.proc_pipe_p.poll(): ctrl_buffer = dstat.state.ser.get_ctrl()
proc_buffer = comm.serial_instance.proc_pipe_p.recv() try:
if ctrl_buffer is not None:
self.current_exp.ctrl_loop(ctrl_buffer)
except AttributeError:
pass
proc_buffer = dstat.state.ser.get_proc()
if proc_buffer is not None:
if proc_buffer in ["DONE", "SERIAL_ERROR", "ABORT"]: if proc_buffer in ["DONE", "SERIAL_ERROR", "ABORT"]:
self.experiment_done() self.experiment_done()
if proc_buffer == "SERIAL_ERROR": if proc_buffer == "SERIAL_ERROR":
...@@ -663,9 +657,7 @@ class Main(object): ...@@ -663,9 +657,7 @@ class Main(object):
else: else:
logger.warning("Unrecognized experiment return code: %s", logger.warning("Unrecognized experiment return code: %s",
proc_buffer) proc_buffer)
return False return False
return True return True
except EOFError as err: except EOFError as err:
...@@ -677,63 +669,40 @@ class Main(object): ...@@ -677,63 +669,40 @@ class Main(object):
self.experiment_done() self.experiment_done()
return False return False
def experiment_running_plot(self): def experiment_running_plot(self, force_refresh=False):
"""Plot all data in current_exp.data. """Plot all data in current_exp.data.
Run in GTK main loop. Always returns True so must be manually Run in GTK main loop. Always returns True so must be manually
removed from GTK's queue. removed from GTK's queue.
""" """
if self.line > self.lastline: for plot in self.current_exp.plots:
self.plot.addline() if (plot.scan_refresh and self.line > self.lastline):
while self.line > self.lastline:
# make sure all of last line is added
plot.updateline(self.current_exp, self.lastline)
self.lastline += 1
plot.updateline(self.current_exp, self.line)
plot.redraw()
else:
while self.line > self.lastline:
# make sure all of last line is added # make sure all of last line is added
self.plot.updateline(self.current_exp, self.lastline) plot.updateline(self.current_exp, self.lastline)
self.lastline = self.line self.lastline += 1
self.plot.updateline(self.current_exp, self.line) plot.updateline(self.current_exp, self.line)
self.plot.redraw()
if plot.continuous_refresh is True or force_refresh is True:
plot.redraw()
return True return True
def experiment_done(self): def experiment_done(self, widget=None):
"""Clean up after data acquisition is complete. Update plot and """Clean up after data acquisition is complete. Update plot and
copy data to raw data tab. Saves data if autosave enabled. copy data to raw data tab. Saves data if autosave enabled.
""" """
self.current_exp.time = datetime.now()
gobject.source_remove(self.experiment_proc[0])
gobject.source_remove(self.plot_proc) # stop automatic plot update
self.experiment_running_plot() # make sure all data updated on plot
self.databuffer.set_text("")
self.databuffer.place_cursor(self.databuffer.get_start_iter())
self.rawbuffer.insert_at_cursor("\n")
self.rawbuffer.set_text("")
self.rawbuffer.place_cursor(self.rawbuffer.get_start_iter())
# Shutter stuff
if (self.current_exp.parameters['shutter_true'] and
self.current_exp.parameters['sync_true']):
self.ft_plot.updateline(self.current_exp, 0)
self.ft_plot.redraw()
line_buffer = []
for scan in self.current_exp.data['ft']:
for dimension in scan:
for i in range(len(dimension)):
try: try:
line_buffer[i] += "%s " % dimension[i]
except IndexError:
line_buffer.append("")
line_buffer[i] += "%s " % dimension[i]
for i in line_buffer:
self.databuffer.insert_at_cursor("%s\n" % i)
# Run Analysis # Run Analysis
analysis.do_analysis(self.current_exp) analysis.do_analysis(self.current_exp)
# Write DStat commands # Write DStat commands
for i in self.current_exp.commands: self.info_page.set_text(self.current_exp.get_info_text())
self.rawbuffer.insert_at_cursor(i)
self.rawbuffer.insert_at_cursor("\n")
try: try:
self.statusbar.push( self.statusbar.push(
...@@ -757,48 +726,46 @@ class Main(object): ...@@ -757,48 +726,46 @@ class Main(object):
) )
for i in analysis_buffer: for i in analysis_buffer:
self.rawbuffer.insert_at_cursor("%s\n" % i) self.info_page.add_line(i)
line_buffer = []
for scan in self.current_exp.data['data']: self.data_view.add_exp(self.current_exp)
for dimension in scan:
for i in range(len(dimension)):
try:
line_buffer[i] += "%s " % dimension[i]
except IndexError:
line_buffer.append("")
line_buffer[i] += "%s " % dimension[i]
for i in line_buffer:
self.rawbuffer.insert_at_cursor("%s\n" % i)
# Autosaving # Autosaving
if self.autosave_checkbox.get_active(): if self.autosave_checkbox.get_active():
save.autoSave(self.current_exp, save.autoSave(self.current_exp,
self.autosavedir_button.get_filename(), self.autosavedir_button.get_filename().decode('utf-8'),
self.autosavename.get_text() self.autosavename.get_text()
) )
save.autoPlot(self.current_exp, save.autoPlot(self.current_exp,
self.autosavedir_button.get_filename(), self.autosavedir_button.get_filename().decode('utf-8'),
self.autosavename.get_text() self.autosavename.get_text()
) )
# uDrop # uDrop
# UI stuff # UI stuff
finally:
self.metadata = None # Reset metadata
self.spinner.stop() self.spinner.stop()
self.startbutton.set_sensitive(True) self.exp_progressbar.set_fraction(0)
self.stopbutton.set_sensitive(False) self.stopbutton.set_sensitive(False)
self.start_ocp() self.start_ocp()
self.completed_experiment_ids[self.active_experiment_id] =\ self.completed_experiment_ids[self.active_experiment_id] =\
datetime.utcnow() datetime.utcnow()
# Temporary fix for weird drawing bug on windows Gtk+3
if platform.system() == 'Windows':
position = self.window.get_position()
self.window.hide()
self.window.show()
self.window.move(*position)
def on_pot_stop_clicked(self, data=None): def on_pot_stop_clicked(self, data=None):
"""Stop current experiment. Signals experiment process to stop.""" """Stop current experiment. Signals experiment process to stop."""
try: try:
comm.serial_instance.ctrl_pipe_p.send('a') dstat.state.ser.stop_exp()
except AttributeError: except AttributeError:
pass pass
...@@ -865,18 +832,23 @@ class Main(object): ...@@ -865,18 +832,23 @@ class Main(object):
self.plugin.reset() self.plugin.reset()
# Periodically process outstanding message received on plugin sockets. # Periodically process outstanding message received on plugin sockets.
self.plugin_timeout_id = gtk.timeout_add(500, self.plugin_timeout_id = Gtk.timeout_add(500,
self.plugin.check_sockets) self.plugin.check_sockets)
def cleanup_plugin(self): def cleanup_plugin(self):
if self.plugin_timeout_id is not None: if self.plugin_timeout_id is not None:
gobject.source_remove(self.plugin_timeout_id) GObject.source_remove(self.plugin_timeout_id)
if self.plugin is not None: if self.plugin is not None:
self.plugin = None self.plugin = None
if __name__ == "__main__": if __name__ == "__main__":
multiprocessing.freeze_support() multiprocessing.freeze_support()
gobject.threads_init() GObject.threads_init()
MAIN = Main() MAIN = Main()
gtk.main() mainloop = GObject.MainLoop()
try:
mainloop.run()
except KeyboardInterrupt:
logger.info('Ctrl+C hit, quitting')
MAIN.quit()
#!/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 <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/>.
import logging
from errors import InputError
logger = logging.getLogger("dstat.parameter_test")
def lsv_test(params):
"""Test LSV parameters for sanity"""
parameters = {}
for i in params:
try:
parameters[i] = int(params[i])
except TypeError:
pass
if (parameters['clean_mV'] > 1499 or
parameters['clean_mV'] < -1500):
raise InputError(parameters['clean_mV'],
"Clean potential exceeds hardware limits.")
if (parameters['dep_mV'] > 1499 or
parameters['dep_mV'] < -1500):
raise InputError(parameters['dep_mV'],
"Deposition potential exceeds hardware limits.")
if (parameters['clean_s'] < 0):
raise InputError(parameters['clean_s'],
"Clean time cannot be negative.")
if (parameters['dep_s'] < 0):
raise InputError(parameters['dep_s'],
"Deposition time cannot be negative.")
if (parameters['start'] > 1499 or parameters['start'] < -1500):
raise InputError(parameters['start'],
"Start parameter exceeds hardware limits.")
if (parameters['stop'] > 1499 or parameters['stop'] < -1500):
raise InputError(parameters['stop'],
"Stop parameter exceeds hardware limits.")
if (parameters['slope'] > 2000 or parameters['slope'] < 1):
raise InputError(parameters['slope'],
"Slope parameter exceeds hardware limits.")
if parameters['start'] == parameters['stop']:
raise InputError(parameters['start'],
"Start cannot equal Stop.")
def cv_test(params):
"""Test CV parameters for sanity"""
parameters = {}
for i in params:
try:
parameters[i] = int(params[i])
except TypeError:
pass
if (parameters['clean_mV'] > 1499 or
parameters['clean_mV'] < -1500):
raise InputError(parameters['clean_mV'],
"Clean potential exceeds hardware limits.")
if (parameters['dep_mV'] > 1499 or
parameters['dep_mV'] < -1500):
raise InputError(parameters['dep_mV'],
"Deposition potential exceeds hardware limits.")
if (parameters['clean_s'] < 0):
raise InputError(parameters['clean_s'],
"Clean time cannot be negative.")
if (parameters['dep_s'] < 0):
raise InputError(parameters['dep_s'],
"Deposition time cannot be negative.")
if (parameters['start'] > 1499 or parameters['start'] < -1500):
raise InputError(parameters['start'],
"Start parameter exceeds hardware limits.")
if (parameters['slope'] > 2000 or parameters['slope'] < 1):
raise InputError(parameters['slope'],
"Slope parameter exceeds hardware limits.")
if (parameters['v1'] > 1499 or parameters['v1'] < -1500):
raise InputError(parameters['v1'],
"Vertex 1 parameter exceeds hardware limits.")
if (parameters['v2'] > 1499 or parameters['v2'] < -1500):
raise InputError(parameters['v2'],
"Vertex 2 parameter exceeds hardware limits.")
if (parameters['scans'] < 1 or parameters['scans'] > 255):
raise InputError(parameters['scans'],
"Scans parameter outside limits.")
if parameters['v1'] == parameters['v2']:
raise InputError(parameters['v1'],
"Vertex 1 cannot equal Vertex 2.")
def swv_test(params):
"""Test SWV parameters for sanity"""
parameters = {}
for i in params:
try:
parameters[i] = int(params[i])
except TypeError:
pass
if params['cyclic_true'] :
if int(params['scans']) < 1:
raise InputError(params['scans'],
"Must have at least one scan.")
else:
params['scans'] = 0
# check parameters are within hardware limits (doesn't
# check if pulse will go out of bounds, but instrument
# checks this (I think))
if (parameters['clean_mV'] > 1499 or
parameters['clean_mV'] < -1500):
raise InputError(parameters['clean_mV'],
"Clean potential exceeds hardware limits.")
if (parameters['dep_mV'] > 1499 or
parameters['dep_mV'] < -1500):
raise InputError(parameters['dep_mV'],
"Deposition potential exceeds hardware limits.")
if (parameters['clean_s'] < 0):
raise InputError(parameters['clean_s'],
"Clean time cannot be negative.")
if (parameters['dep_s'] < 0):
raise InputError(parameters['dep_s'],
"Deposition time cannot be negative.")
if (parameters['start'] > 1499 or parameters['start'] < -1500):
raise InputError(parameters['start'],
"Start parameter exceeds hardware limits.")
if (parameters['step'] > 200 or parameters['step'] < 1):
raise InputError(parameters['step'],
"Step height parameter exceeds hardware limits.")
if (parameters['stop'] > 1499 or parameters['stop'] < -1500):
raise InputError(parameters['stop'],
"Stop parameter exceeds hardware limits.")
if (parameters['pulse'] > 150 or parameters['pulse'] < 1):
raise InputError(parameters['pulse'],
"Pulse height parameter exceeds hardware limits.")
if (parameters['freq'] < 1 or parameters['freq'] > 1000):
raise InputError(parameters['freq'],
"Frequency parameter outside limits.")
if parameters['start'] == parameters['stop']:
raise InputError(parameters['start'],
"Start cannot equal Stop.")
def dpv_test(params):
"""Test DPV parameters for sanity"""
parameters = {}
for i in params:
try:
parameters[i] = int(params[i])
except TypeError:
pass
if (parameters['clean_mV'] > 1499 or
parameters['clean_mV'] < -1500):
raise InputError(parameters['clean_mV'],
"Clean potential exceeds hardware limits.")
if (parameters['dep_mV'] > 1499 or
parameters['dep_mV'] < -1500):
raise InputError(parameters['dep_mV'],
"Deposition potential exceeds hardware limits.")
if (parameters['clean_s'] < 0):
raise InputError(parameters['clean_s'],
"Clean time cannot be negative.")
if (parameters['dep_s'] < 0):
raise InputError(parameters['dep_s'],
"Deposition time cannot be negative.")
if (parameters['start'] > 1499 or parameters['start'] < -1500):
raise InputError(parameters['start'],
"Start parameter exceeds hardware limits.")
if (parameters['step'] > 200 or parameters['step'] < 1):
raise InputError(parameters['step'],
"Step height parameter exceeds hardware limits.")
if (parameters['stop'] > 1499 or parameters['stop'] < -1500):
raise InputError(parameters['stop'],
"Stop parameter exceeds hardware limits.")
if (parameters['pulse'] > 150 or parameters['pulse'] < 1):
raise InputError(parameters['pulse'],
"Pulse height parameter exceeds hardware limits.")
if (parameters['period'] < 1 or parameters['period'] > 1000):
raise InputError(parameters['period'],
"Period parameter outside limits.")
if (parameters['width'] < 1 or parameters['width'] > 1000):
raise InputError(parameters['width'],
"Width parameter outside limits.")
if parameters['period'] <= parameters['width']:
raise InputError(parameters['width'],
"Width must be less than period.")
if parameters['start'] == parameters['stop']:
raise InputError(parameters['start'],
"Start cannot equal Stop.")
def pd_test(parameters):
"""Test PD parameters for sanity"""
if (int(parameters['time']) <= 0):
raise InputError(parameters['time'],
"Time must be greater than zero.")
if (int(parameters['time']) > 65535):
raise InputError(parameters['time'],
"Time must fit in 16-bit counter.")
if (parameters['sync_true'] and parameters['shutter_true']):
if (float(parameters['sync_freq']) > 30 or
float(parameters['sync_freq']) <= 0):
raise InputError(parameters['sync_freq'],
"Frequency must be between 0 and 30 Hz.")
if (float(parameters['fft_start']) < 0 or
float(parameters['fft_start']) > float(parameters['time'])-1):
raise InputError(parameters['fft_start'],
"FFT must start between 0 and time-1.")
if float(parameters['fft_int']) < 0:
raise InputError(
parameters['fft_int'],
"Integral bandwidth must be greater than 0"
)
def pot_test(params):
"""Test POT parameters for sanity"""
parameters = {}
for i in params:
try:
parameters[i] = int(params[i])
except TypeError:
pass
if (int(parameters['time']) <= 0):
raise InputError(parameters['time'],
"Time must be greater than zero.")
if (int(parameters['time']) > 65535):
raise InputError(parameters['time'],
"Time must fit in 16-bit counter.")
\ No newline at end of file