Skip to content
...@@ -29,15 +29,17 @@ except ImportError: ...@@ -29,15 +29,17 @@ except ImportError:
print "ERR: GTK not available" print "ERR: GTK not available"
sys.exit(1) 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): class Experiments(GObject.Object):
__gsignals__ = { __gsignals__ = {
'run_utility': (GObject.SIGNAL_RUN_FIRST, None, ()), 'run_utility': (GObject.SIGNAL_RUN_FIRST, None, ()),
'done_utility': (GObject.SIGNAL_RUN_FIRST, None, ()) 'done_utility': (GObject.SIGNAL_RUN_FIRST, None, ())
} }
def __init__(self, builder): def __init__(self, builder):
super(Experiments,self).__init__() super(Experiments,self).__init__()
self.builder = builder self.builder = builder
...@@ -46,12 +48,13 @@ class Experiments(GObject.Object): ...@@ -46,12 +48,13 @@ class Experiments(GObject.Object):
# (experiment index in UI, experiment) # (experiment index in UI, experiment)
classes = {c.id : c() # Make class instances classes = {c.id : c() # Make class instances
for _, c in inspect.getmembers(exp, inspect.isclass) for _, c in inspect.getmembers(exp_int, inspect.isclass)
if issubclass(c, exp.ExpInterface) if issubclass(c, exp_int.ExpInterface)
and c is not exp.ExpInterface and c is not exp_int.ExpInterface
} }
self.classes = OrderedDict(sorted(classes.items())) self.classes = OrderedDict(sorted(classes.items()))
# 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 = {}
...@@ -86,6 +89,9 @@ class Experiments(GObject.Object): ...@@ -86,6 +89,9 @@ class Experiments(GObject.Object):
self.set_exp(self.expcombobox.get_active_id()) self.set_exp(self.expcombobox.get_active_id())
def setup_exp(self, parameters): def setup_exp(self, parameters):
"""Takes parameters.
Returns experiment instance.
"""
exp = self.classes[self.expcombobox.get_active_id()] exp = self.classes[self.expcombobox.get_active_id()]
try: try:
exp.param_test(parameters) exp.param_test(parameters)
...@@ -94,7 +100,7 @@ class Experiments(GObject.Object): ...@@ -94,7 +100,7 @@ class Experiments(GObject.Object):
"Experiment {} has no defined parameter test.".format( "Experiment {} has no defined parameter test.".format(
exp.name) exp.name)
) )
return exp.experiment(parameters) return exp.get_experiment(parameters)
def hide_exps(self): def hide_exps(self):
for key in self.containers: for key in self.containers:
...@@ -117,4 +123,7 @@ class Experiments(GObject.Object): ...@@ -117,4 +123,7 @@ class Experiments(GObject.Object):
return self.classes[experiment].params return self.classes[experiment].params
def set_params(self, experiment, parameters): def set_params(self, experiment, parameters):
try:
self.classes[experiment].params = parameters 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 import logging
logger = logging.getLogger("dstat.interface.plot_ui") logger = logging.getLogger(__name__)
try: try:
import gi import gi
......
...@@ -22,6 +22,9 @@ from __future__ import division, absolute_import, print_function, unicode_litera ...@@ -22,6 +22,9 @@ from __future__ import division, absolute_import, print_function, unicode_litera
import io import io
import os import os
import logging
logger = logging.getLogger(__name__)
try: try:
import gi import gi
...@@ -31,12 +34,9 @@ except ImportError: ...@@ -31,12 +34,9 @@ except ImportError:
print("ERR: GTK not available") print("ERR: GTK not available")
sys.exit(1) sys.exit(1)
import numpy as np import numpy as np
import logging
logger = logging.getLogger("dstat.interface.save")
from errors import InputError, VarError from ..errors import InputError, VarError
from params import save_params, load_params from ..params import save_params, load_params
def manSave(current_exp): def manSave(current_exp):
fcd = Gtk.FileChooserDialog("Save…", None, Gtk.FileChooserAction.SAVE, fcd = Gtk.FileChooserDialog("Save…", None, Gtk.FileChooserAction.SAVE,
......
...@@ -23,14 +23,14 @@ import yaml ...@@ -23,14 +23,14 @@ import yaml
from errors import InputError from errors import InputError
logger = logging.getLogger('dstat.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."""
selection = window.exp_window.expcombobox.get_active_id() selection = window.exp_window.expcombobox.get_active_id()
parameters = {} parameters = {'experiment_index' : selection}
parameters['experiment_index'] = selection
try: try:
parameters['version'] = window.version parameters['version'] = window.version
...@@ -46,6 +46,7 @@ def get_params(window): ...@@ -46,6 +46,7 @@ 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) logger.info("Save to: %s", path)
...@@ -54,6 +55,7 @@ def save_params(window, path): ...@@ -54,6 +55,7 @@ def save_params(window, path):
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."""
...@@ -68,6 +70,7 @@ def load_params(window, path): ...@@ -68,6 +70,7 @@ def load_params(window, path):
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 'experiment_index' in params:
......
...@@ -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+)*)?'
......
__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')
This diff is collapsed.
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