Skip to content
......@@ -29,15 +29,17 @@ except ImportError:
print "ERR: GTK not available"
sys.exit(1)
import interface.exp_int as exp
from . import exp_int
logger = logging.getLogger(__name__)
logger = logging.getLogger("dstat.interface.exp_window")
class Experiments(GObject.Object):
__gsignals__ = {
'run_utility': (GObject.SIGNAL_RUN_FIRST, None, ()),
'done_utility': (GObject.SIGNAL_RUN_FIRST, None, ())
}
def __init__(self, builder):
super(Experiments,self).__init__()
self.builder = builder
......@@ -46,12 +48,13 @@ class Experiments(GObject.Object):
# (experiment index in UI, experiment)
classes = {c.id : c() # Make class instances
for _, c in inspect.getmembers(exp, inspect.isclass)
if issubclass(c, exp.ExpInterface)
and c is not exp.ExpInterface
for _, c in inspect.getmembers(exp_int, inspect.isclass)
if issubclass(c, exp_int.ExpInterface)
and c is not exp_int.ExpInterface
}
self.classes = OrderedDict(sorted(classes.items()))
# fill exp_section
exp_section = self.builder.get_object('exp_section_box')
self.containers = {}
......@@ -86,6 +89,9 @@ class Experiments(GObject.Object):
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)
......@@ -94,7 +100,7 @@ class Experiments(GObject.Object):
"Experiment {} has no defined parameter test.".format(
exp.name)
)
return exp.experiment(parameters)
return exp.get_experiment(parameters)
def hide_exps(self):
for key in self.containers:
......@@ -117,4 +123,7 @@ class Experiments(GObject.Object):
return self.classes[experiment].params
def set_params(self, experiment, parameters):
try:
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
import logging
logger = logging.getLogger("dstat.interface.plot_ui")
logger = logging.getLogger(__name__)
try:
import gi
......
......@@ -22,6 +22,9 @@ from __future__ import division, absolute_import, print_function, unicode_litera
import io
import os
import logging
logger = logging.getLogger(__name__)
try:
import gi
......@@ -31,12 +34,9 @@ except ImportError:
print("ERR: GTK not available")
sys.exit(1)
import numpy as np
import logging
logger = logging.getLogger("dstat.interface.save")
from errors import InputError, VarError
from params import save_params, load_params
from ..errors import InputError, VarError
from ..params import save_params, load_params
def manSave(current_exp):
fcd = Gtk.FileChooserDialog("Save…", None, Gtk.FileChooserAction.SAVE,
......
......@@ -23,14 +23,14 @@ import yaml
from errors import InputError
logger = logging.getLogger('dstat.params')
logger = logging.getLogger(__name__)
def get_params(window):
"""Fetches and returns dict of all parameters for saving."""
selection = window.exp_window.expcombobox.get_active_id()
parameters = {}
parameters['experiment_index'] = selection
parameters = {'experiment_index' : selection}
try:
parameters['version'] = window.version
......@@ -46,6 +46,7 @@ def get_params(window):
return parameters
def save_params(window, path):
"""Fetches current params and saves to path."""
logger.info("Save to: %s", path)
......@@ -54,6 +55,7 @@ def save_params(window, path):
with open(path, 'w') as f:
yaml.dump(params, f)
def load_params(window, path):
"""Loads params from a path into UI elements."""
......@@ -68,6 +70,7 @@ def load_params(window, path):
params = yaml.load(f)
set_params(window, params)
def set_params(window, params):
window.adc_pot.params = params
if 'experiment_index' in params:
......
......@@ -48,9 +48,11 @@ __all__ = ('getVersion')
import re
import subprocess
import sys
import os.path
import inspect
RELEASE_VERSION_FILE = 'RELEASE-VERSION'
RELEASE_VERSION_FILE = '{}/RELEASE-VERSION'.format(
os.path.dirname(os.path.abspath(inspect.stack()[0][1])))
# http://www.python.org/dev/peps/pep-0386/
_PEP386_SHORT_VERSION_RE = r'\d+(?:\.\d+)+(?:(?:[abc]|rc)\d+(?:\.\d+)*)?'
......
__all__ = []
import pkgutil
import inspect
for loader, name, is_pkg in pkgutil.walk_packages(__path__):
module = loader.find_module(name).load_module(name)
for name, value in inspect.getmembers(module):
if name.startswith('__'):
continue
globals()[name] = value
__all__.append(name)
\ No newline at end of file
import time
import struct
from experiments.experiment_template import PlotBox, Experiment
class LSVExp(Experiment):
"""Linear Scan Voltammetry experiment"""
id = 'lsv'
def setup(self):
super(LSVExp, self).setup()
self.datatype = "linearData"
self.datalength = 2
self.databytes = 6 # uint16 + int32
self.plot_format['current_voltage']['xlims'] = (
int(self.parameters['start']),
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] += " "
\ 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,68 +20,74 @@
""" GUI Interface for Wheeler Lab DStat """
from __future__ import division, absolute_import, print_function, unicode_literals
import sys
import os
import platform
import multiprocessing
import uuid
from copy import deepcopy
from collections import OrderedDict
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:
# import pygtk
# pygtk.require('2.0')
# except ImportError:
# print "ERR: PyGTK 2.0 not available"
# sys.exit(1)
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
except ImportError:
print "ERR: GTK not available"
print("ERR: GTK not available")
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 experiments as exp
import interface.exp_window as exp_window
import interface.adc_pot as adc_pot
import interface.plot_ui
import interface.data_view
import plot
import params
import parameter_test
import analysis
import zmq
import state
from errors import InputError
mod_dir = os.path.dirname(os.path.abspath(__file__))
conf_path = os.path.join(os.path.expanduser("~"), '.dstat-interface')
from plugin import DstatPlugin, get_hub_uri
# if __name__ == "__parents_main__": # Only runs for forking emulation on win
# sys.path.append(mod_dir)
# Setup Logging
root_logger = logging.getLogger("dstat")
root_logger.setLevel(level=logging.INFO)
logger = logging.getLogger(__name__)
core_logger = logging.getLogger("dstat_interface.core")
loggers = [logger, core_logger]
log_handler = logging.StreamHandler()
log_formatter = logging.Formatter(
fmt='%(asctime)s %(levelname)s: [%(name)s] %(message)s',
datefmt='%H:%M:%S'
)
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 """
__gsignals__ = {
b'exp_start': (GObject.SIGNAL_RUN_FIRST, None, ()),
b'exp_stop': (GObject.SIGNAL_RUN_FIRST, None, ())
}
def __init__(self):
super(Main, self).__init__()
self.builder = Gtk.Builder()
self.builder.add_from_file('interface/dstatinterface.glade')
self.builder.add_from_file(
os.path.join(mod_dir, 'core/interface/dstatinterface.glade'))
self.builder.connect_signals(self)
self.cell = Gtk.CellRendererText()
......@@ -92,6 +98,8 @@ class Main(object):
self.aboutdialog = self.builder.get_object('aboutdialog1')
self.stopbutton = self.builder.get_object('pot_stop')
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.error_context_id = self.statusbar.get_context_id("error")
......@@ -111,8 +119,8 @@ class Main(object):
# Setup Plots
self.plot_notebook = self.builder.get_object('plot_notebook')
self.main_notebook = self.builder.get_object('main_notebook')
self.data_view = interface.data_view.DataPage(self.main_notebook)
self.info_page = interface.data_view.InfoPage(self.main_notebook)
self.data_view = data_view.DataPage(self.main_notebook)
self.info_page = data_view.InfoPage(self.main_notebook)
# fill adc_pot_box
self.adc_pot_box = self.builder.get_object('gain_adc_box')
......@@ -127,9 +135,8 @@ class Main(object):
self.serial_combobox = self.builder.get_object('serial_combobox')
self.serial_combobox.pack_start(self.cell, True)
self.serial_combobox.add_attribute(self.cell, 'text', 0)
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:
self.serial_liststore.append([i])
......@@ -140,6 +147,29 @@ class Main(object):
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
try:
ver = getVersion()
......@@ -152,7 +182,6 @@ class Main(object):
self.mainwindow.show_all()
self.exp_window.hide_exps()
self.expnumber = 0
self.connected = False
......@@ -186,69 +215,100 @@ class Main(object):
def quit(self):
"""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()
except KeyError:
pass
mainloop.quit()
def on_gtk_about_activate(self, menuitem, data=None):
"""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()
def on_menu_analysis_options_activate(self, menuitem, data=None):
self.analysis_opt_window.show()
def on_serial_refresh_clicked(self, data=None):
"""Refresh list of serial devices."""
try:
self.serial_devices.refresh()
self.serial_liststore.clear()
except ValueError:
logger.warning("No DStats found")
for i in self.serial_devices.ports:
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."""
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:
self.serial_connect.set_sensitive(False)
state.dstat_version = comm.version_check(self.serial_liststore.get_value(
self.serial_combobox.get_active_iter(), 0))
self.serial_pmt_connect.set_sensitive(False)
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(state.dstat_version) == 2:
self.statusbar.push(self.error_context_id,
"Communication Error")
return
self.statusbar.remove_all(self.error_context_id)
else:
self.adc_pot.set_version(state.dstat_version)
self.statusbar.push(self.error_context_id,
"".join(["DStat version: ",
str(state.dstat_version[0]),
".", str(state.dstat_version[1])])
self.adc_pot.set_version()
self.statusbar.push(
self.message_context_id,
"DStat version: {}".format(
dstat.state.dstat_version.base_version)
)
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.connected = True
self.serial_connect.set_sensitive(False)
self.serial_pmt_connect.set_sensitive(False)
self.serial_disconnect.set_sensitive(True)
except AttributeError as err:
logger.warning("AttributeError: %s", err)
self.serial_connect.set_sensitive(True)
except TypeError as err:
logger.warning("TypeError: %s", err)
except:
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:
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:
logger.info("No previous parameters found.")
......@@ -262,46 +322,53 @@ class Main(object):
self.stop_ocp()
else:
self.on_pot_stop_clicked()
state.ser.send_ctrl("DISCONNECT")
state.ser.disconnect()
dstat.state.ser.disconnect()
except AttributeError as err:
logger.warning("AttributeError: %s", err)
pass
if self.pmt_mode is True:
self.adc_pot.ui['short_true'].set_sensitive(True)
self.pmt_mode = False
self.connected = False
self.serial_connect.set_sensitive(True)
self.serial_pmt_connect.set_sensitive(True)
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)
def on_pmt_mode_clicked(self, data=None):
"""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()
dstat.state.reset()
def start_ocp(self, data=None):
"""Start OCP measurements."""
if state.dstat_version[0] >= 1 and state.dstat_version[1] >= 2:
if dstat.state.dstat_version >= parse_version('1.2'):
# Flush data pipe
state.ser.flush_data()
dstat.state.ser.flush_data()
if self.pmt_mode == True:
if self.pmt_mode is True:
logger.info("Start PMT idle mode")
state.ser.start_exp(exp.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:
logger.info("Start OCP")
state.ser.start_exp(exp.OCPExp())
dstat.state.ser.start_exp(idle.OCPExp())
self.ocp_proc = (GObject.timeout_add(300, self.ocp_running_data),
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:
logger.info("OCP measurements not supported on v1.1 boards.")
......@@ -310,23 +377,37 @@ class Main(object):
def stop_ocp(self, data=None):
"""Stop OCP measurements."""
if state.dstat_version[0] >= 1 and state.dstat_version[1] >= 2:
if dstat.state.dstat_version >= parse_version('1.2'):
if self.pmt_mode == True:
logger.info("Stop PMT idle mode")
else:
logger.info("Stop OCP")
state.ser.send_ctrl('a')
dstat.state.ser.send_ctrl('a')
for i in self.ocp_proc:
GObject.source_remove(i)
while self.ocp_running_proc():
pass
self.ocp_is_running = False
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:
logger.error("OCP measurements not supported on v1.1 boards.")
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):
"""Receive OCP value from experiment process and update ocp_disp field
......@@ -337,7 +418,7 @@ class Main(object):
"""
try:
incoming = state.ser.get_data()
incoming = dstat.state.ser.get_data()
while incoming is not None:
if isinstance(incoming, basestring): # test if incoming is str
self.on_serial_disconnect_clicked()
......@@ -348,10 +429,11 @@ class Main(object):
"{0:.3f}".format(incoming),
" V"])
self.ocp_disp.set_text(data)
self.ocp_is_running = True
except ValueError:
pass
incoming = state.ser.get_data()
incoming = dstat.state.ser.get_data()
return True
......@@ -370,16 +452,16 @@ class Main(object):
"""
try:
proc_buffer = state.ser.get_proc()
proc_buffer = dstat.state.ser.get_proc()
while proc_buffer is not None:
logger.debug("ocp_running_proc: %s", proc_buffer)
if proc_buffer in ["DONE", "SERIAL_ERROR", "ABORT"]:
if proc_buffer == "SERIAL_ERROR":
self.on_serial_disconnect_clicked()
state.ser.flush_data()
dstat.state.ser.flush_data()
return False
proc_buffer = state.ser.get_proc()
proc_buffer = dstat.state.ser.get_proc()
return True
except EOFError:
......@@ -417,7 +499,7 @@ class Main(object):
self.stop_ocp()
self.statusbar.remove_all(self.error_context_id)
state.ser.flush_data()
dstat.state.ser.flush_data()
parameters = {}
parameters['metadata'] = self.metadata
......@@ -440,30 +522,28 @@ class Main(object):
self.stopbutton.set_sensitive(True)
self.statusbar.remove_all(self.error_context_id)
try:
del self.current_exp
except AttributeError:
pass
callbacks = {'experiment_done' : self.experiment_done,
'progress_update' : self.progress_update}
self.current_exp = self.exp_window.setup_exp(parameters)
interface.plot_ui.replace_notebook_exp(
plot_ui.replace_notebook_exp(
self.plot_notebook, self.current_exp, self.window
)
for plot in self.current_exp.plots:
plot.changetype(self.current_exp)
self.data_view.clear_exps()
self.info_page.clear()
state.ser.start_exp(self.current_exp)
dstat.state.ser.start_exp(self.current_exp)
# Flush data pipe
state.ser.flush_data()
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)
)
dstat.state.ser.flush_data()
self.current_exp.setup_loops(callbacks)
return experiment_id
except ValueError as i:
......@@ -501,6 +581,12 @@ class Main(object):
exceptions()
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):
"""Receive data from experiment process and add to
current_exp.data['data].
......@@ -512,27 +598,35 @@ class Main(object):
function from GTK's queue.
"""
try:
incoming = state.ser.get_data()
incoming = dstat.state.ser.get_data()
while incoming is not None:
try:
self.line, data = incoming
if self.line > self.lastdataline:
newline = True
try:
logger.info("running scan_process()")
self.current_exp.scan_process(self.lastdataline)
except AttributeError:
pass
self.lastdataline = self.line
else:
newline = False
self.current_exp.store_data(incoming, newline)
if newline:
self.experiment_running_plot()
except TypeError:
pass
incoming = state.ser.get_data()
incoming = dstat.state.ser.get_data()
return True
except EOFError as err:
print err
logger.error(err)
self.experiment_done()
return False
except IOError as err:
print err
logger.error(err)
self.experiment_done()
return False
......@@ -546,7 +640,14 @@ class Main(object):
function from GTK's queue.
"""
try:
proc_buffer = state.ser.get_proc()
ctrl_buffer = dstat.state.ser.get_ctrl()
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"]:
self.experiment_done()
......@@ -574,53 +675,32 @@ class Main(object):
removed from GTK's queue.
"""
for plot in self.current_exp.plots:
if self.line > self.lastline:
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 = self.line
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
plot.updateline(self.current_exp, self.lastline)
self.lastline += 1
plot.updateline(self.current_exp, self.line)
if plot.continuous_refresh is True or force_refresh is True:
plot.redraw()
return True
def experiment_done(self):
def experiment_done(self, widget=None):
"""Clean up after data acquisition is complete. Update plot and
copy data to raw data tab. Saves data if autosave enabled.
"""
try:
self.current_exp.experiment_done()
GObject.source_remove(self.experiment_proc[0])
GObject.source_remove(self.plot_proc) # stop automatic plot update
self.experiment_running_plot(force_refresh=True)
# # 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:
# 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
analysis.do_analysis(self.current_exp)
self.current_exp.experiment_done()
# Write DStat commands
self.info_page.set_text(self.current_exp.get_info_text())
......@@ -668,17 +748,24 @@ class Main(object):
self.metadata = None # Reset metadata
self.spinner.stop()
self.startbutton.set_sensitive(True)
self.exp_progressbar.set_fraction(0)
self.stopbutton.set_sensitive(False)
self.start_ocp()
self.completed_experiment_ids[self.active_experiment_id] =\
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):
"""Stop current experiment. Signals experiment process to stop."""
try:
state.ser.stop_exp()
dstat.state.ser.stop_exp()
except AttributeError:
pass
......
mr_db @ 72481e58
Subproject commit 72481e585d92d0adab6537718a4d18164df1b445
#!/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"""
test_parameters = ['clean_mV', 'dep_mV', 'clean_s', 'dep_s', 'start',
'stop', 'slope']
parameters = {}
for i in params:
if i in test_parameters:
parameters[i] = int(params[i])
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"""
test_parameters = ['clean_mV', 'dep_mV', 'clean_s', 'dep_s', 'start',
'stop', 'slope', 'v1', 'v2', 'scans']
parameters = {}
for i in params:
if i in test_parameters:
parameters[i] = int(params[i])
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"""
test_parameters = ['clean_mV', 'dep_mV', 'clean_s', 'dep_s', 'start',
'stop', 'step', 'pulse', 'freq']
parameters = {}
for i in params:
if i in test_parameters:
parameters[i] = int(params[i])
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"""
test_parameters = ['clean_mV', 'dep_mV', 'clean_s', 'dep_s', 'start',
'stop', 'step', 'pulse', 'period', 'width']
parameters = {}
for i in params:
if i in test_parameters:
parameters[i] = int(params[i])
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"""
test_parameters = ['time']
parameters = {}
for i in params:
if i in test_parameters:
parameters[i] = int(params[i])
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