diff --git a/dstat_interface/dstat-interface.bat b/dstat_interface/dstat-interface.bat index 031c2147b93a3881ae054470d674f286c8c25e53..da83e27ab1d09b0b02466c89f033c52a7621b484 100644 --- a/dstat_interface/dstat-interface.bat +++ b/dstat_interface/dstat-interface.bat @@ -1 +1 @@ -python .\__main__.py +python .\main.py diff --git a/dstat_interface/dstat_comm.py b/dstat_interface/dstat_comm.py index 59b15d3b9254ab200575f3ab078b638d205797a6..d5edd59e98011c73cccdc7c39bae0ded203342fa 100644 --- a/dstat_interface/dstat_comm.py +++ b/dstat_interface/dstat_comm.py @@ -79,19 +79,6 @@ class SerialConnection(object): self.proc.start() -def call_it(instance, name, args=(), kwargs=None): - """Indirect caller for instance methods and multiprocessing. - - Arguments: - instance -- instance to which the method belongs - name -- method to call - args -- passed to method - kwargs -- passed to method - """ - if kwargs is None: - kwargs = {} - return getattr(instance, name)(*args, **kwargs) - class VersionCheck: def __init__(self): pass @@ -131,7 +118,31 @@ class VersionCheck: 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() + print result + + return buffer + + except: + pass + class Settings: def __init__(self, task, settings=None): self.task = task @@ -201,7 +212,39 @@ class Settings: 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() + + print 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)) + + while serial_instance.proc_pipe_p.recv() != "DONE": + pass + + return + class LightSensor: def __init__(self): pass @@ -238,37 +281,14 @@ class LightSensor: 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() - print result - - return buffer - - except: - pass - - 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()) while serial_instance.proc_pipe_p.recv() != "DONE": @@ -276,32 +296,6 @@ def read_light_sensor(): return serial_instance.data_pipe_p.recv() -def read_settings(): - """Tries to contact DStat and get settings. Returns dict of - settings. - """ - - global settings - settings = {} - serial_instance.proc_pipe_p.send(Settings(task='r')) - settings = serial_instance.data_pipe_p.recv() - - while serial_instance.proc_pipe_p.recv() != "DONE": - pass - - return - -def write_settings(): - """Tries to write settings to DStat from global settings var. - """ - - serial_instance.proc_pipe_p.send(Settings(task='w')) - - while serial_instance.proc_pipe_p.recv() != "DONE": - pass - - return - class delayedSerial(serial.Serial): """Extends Serial.write so that characters are output individually @@ -329,12 +323,6 @@ class Experiment(object): """Store and acquire a potentiostat experiment. Meant to be subclassed to by different experiment types and not used instanced directly. """ - # def run_wrapper(self, *argv): - # """Execute experiment indirectly using call_it to bypass lack of fork() - # on Windows for multiprocessing. - # """ - # self.proc = mp.Process(target=call_it, args=(self, 'run', argv)) - # self.proc.start() def __init__(self, parameters): """Adds commands for gain and ADC.""" @@ -399,16 +387,14 @@ class Experiment(object): self.serial.write(i) if not self.serial_handler(): status = "ABORT" - break - + + self.data_postprocessing() except serial.SerialException: status = "SERIAL_ERROR" - finally: - self.data_postprocessing() while self.ctrl_pipe.poll(): self.ctrl_pipe.recv() - return status + return status def serial_handler(self): """Handles incoming serial transmissions from DStat. Returns False @@ -468,6 +454,91 @@ class Experiment(object): """ 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(): + print "serial_handler ctrl_pipe" + input = self.ctrl_pipe.recv() + print input + if input == ('a' or "DISCONNECT"): + self.serial.write('a') + print "ABORT pressed!" + return False + + for line in self.serial: + if self.ctrl_pipe.poll(): + if self.ctrl_pipe.recv() == 'a': + self.serial.write('a') + print "ABORT pressed" + return False + + if line.startswith('B'): + self.data.append(self.data_handler( + self.serial.read(size=self.databytes))) + + elif line.lstrip().startswith("#"): + print line + + elif line.lstrip().startswith("no"): + print line + 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): @@ -754,4 +825,21 @@ class OCPExp(Experiment): scan, data = data_input # 2*uint16 + int32 seconds, milliseconds, voltage = struct.unpack('<HHl', data) - return (voltage/5.592405e6) \ No newline at end of file + return (voltage/5.592405e6) + +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)) + print 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 diff --git a/dstat_interface/interface/calib.glade b/dstat_interface/interface/calib.glade index f5db97b6f4dc66772750ac69f76d29653a5ff552..2c72437a2aecb5ed935bd60f6206f56932605cae 100644 --- a/dstat_interface/interface/calib.glade +++ b/dstat_interface/interface/calib.glade @@ -26,10 +26,20 @@ <property name="n_rows">13</property> <property name="n_columns">2</property> <property name="homogeneous">True</property> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> <child> <object class="GtkEntry" id="time_entry"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="max_length">5</property> <property name="invisible_char">â—</property> <property name="width_chars">8</property> <property name="text" translatable="yes">0</property> @@ -55,6 +65,7 @@ <property name="label" translatable="yes">Measurement Time (s)</property> </object> <packing> + <property name="x_options"/> <property name="y_options">GTK_FILL</property> </packing> </child> @@ -78,6 +89,8 @@ <packing> <property name="top_attach">2</property> <property name="bottom_attach">3</property> + <property name="x_options"/> + <property name="x_padding">10</property> </packing> </child> <child> @@ -102,6 +115,7 @@ <packing> <property name="top_attach">3</property> <property name="bottom_attach">4</property> + <property name="x_options"/> </packing> </child> <child> @@ -113,6 +127,7 @@ <packing> <property name="top_attach">4</property> <property name="bottom_attach">5</property> + <property name="x_options">GTK_EXPAND</property> </packing> </child> <child> @@ -124,6 +139,7 @@ <packing> <property name="top_attach">5</property> <property name="bottom_attach">6</property> + <property name="x_options">GTK_EXPAND</property> </packing> </child> <child> @@ -135,6 +151,7 @@ <packing> <property name="top_attach">6</property> <property name="bottom_attach">7</property> + <property name="x_options">GTK_EXPAND</property> </packing> </child> <child> @@ -146,6 +163,7 @@ <packing> <property name="top_attach">7</property> <property name="bottom_attach">8</property> + <property name="x_options">GTK_EXPAND</property> </packing> </child> <child> @@ -157,6 +175,7 @@ <packing> <property name="top_attach">8</property> <property name="bottom_attach">9</property> + <property name="x_options">GTK_EXPAND</property> </packing> </child> <child> @@ -168,13 +187,14 @@ <packing> <property name="top_attach">9</property> <property name="bottom_attach">10</property> + <property name="x_options">GTK_EXPAND</property> </packing> </child> <child> <object class="GtkEntry" id="30k_entry"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="max_length">4</property> + <property name="max_length">6</property> <property name="invisible_char">â—</property> <property name="width_chars">8</property> <property name="text" translatable="yes">0</property> @@ -199,7 +219,7 @@ <object class="GtkEntry" id="3k_entry"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="max_length">4</property> + <property name="max_length">6</property> <property name="invisible_char">â—</property> <property name="width_chars">8</property> <property name="text" translatable="yes">0</property> @@ -224,7 +244,7 @@ <object class="GtkEntry" id="100_entry"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="max_length">4</property> + <property name="max_length">6</property> <property name="invisible_char">â—</property> <property name="width_chars">8</property> <property name="text" translatable="yes">0</property> @@ -249,7 +269,7 @@ <object class="GtkEntry" id="300k_entry"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="max_length">4</property> + <property name="max_length">6</property> <property name="invisible_char">â—</property> <property name="width_chars">8</property> <property name="text" translatable="yes">0</property> @@ -274,7 +294,7 @@ <object class="GtkEntry" id="3M_entry"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="max_length">4</property> + <property name="max_length">6</property> <property name="invisible_char">â—</property> <property name="width_chars">8</property> <property name="text" translatable="yes">0</property> @@ -299,7 +319,7 @@ <object class="GtkEntry" id="30M_entry"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="max_length">4</property> + <property name="max_length">6</property> <property name="invisible_char">â—</property> <property name="width_chars">8</property> <property name="text" translatable="yes">0</property> @@ -324,7 +344,7 @@ <object class="GtkEntry" id="100M_entry"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="max_length">4</property> + <property name="max_length">6</property> <property name="invisible_char">â—</property> <property name="width_chars">8</property> <property name="text" translatable="yes">0</property> @@ -385,6 +405,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> + <signal name="clicked" handler="on_measure_button_clicked" swapped="no"/> </object> <packing> <property name="left_attach">1</property> @@ -395,15 +416,6 @@ <property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property> </packing> </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> </object> <packing> <property name="expand">False</property> @@ -415,9 +427,10 @@ <object class="GtkLabel" id="label13"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="xpad">20</property> + <property name="xpad">5</property> <property name="ypad">20</property> - <property name="label" translatable="yes">Measure with WE open. Offsets cannot exceed 16 bit unsigned int.</property> + <property name="label" translatable="yes">Measure with WE open. +Offsets cannot exceed 16 bit unsigned int.</property> <attributes> <attribute name="weight" value="bold"/> </attributes> diff --git a/dstat_interface/interface/exp_int.py b/dstat_interface/interface/exp_int.py index 682d375124a92eb63d12f2036256926b08490127..edc13d09478a7c4a929660b35b86fdffc2f098fa 100644 --- a/dstat_interface/interface/exp_int.py +++ b/dstat_interface/interface/exp_int.py @@ -262,40 +262,100 @@ class CAL(ExpInterface): self.entry['R30M'] = self.builder.get_object('30M_entry') self.entry['R100M'] = self.builder.get_object('100M_entry') - def on_read_button_clicked(self, data=None): - __main__.MAIN.on_pot_stop_clicked() + self.buttons = [self.builder.get_object('read_button'), + self.builder.get_object('write_button'), + self.builder.get_object('measure_button')] - gobject.source_remove(__main__.MAIN.ocp_proc) - dstat_comm.read_settings() - - self.entry['R100'].set_text(str( - dstat_comm.settings['r100_trim'][1])) - self.entry['R3k'].set_text(str( - dstat_comm.settings['r3k_trim'][1])) - self.entry['R30k'].set_text(str( - dstat_comm.settings['r30k_trim'][1])) - self.entry['R300k'].set_text(str( - dstat_comm.settings['r300k_trim'][1])) - self.entry['R3M'].set_text(str( - dstat_comm.settings['r3M_trim'][1])) - self.entry['R30M'].set_text(str( - dstat_comm.settings['r30M_trim'][1])) - self.entry['R100M'].set_text(str( - dstat_comm.settings['r100M_trim'][1])) - - __main__.MAIN.start_ocp() + def on_read_button_clicked(self, data=None): + for i in self.buttons: + i.set_sensitive(False) + + try: + __main__.MAIN.on_pot_stop_clicked() + gobject.source_remove(__main__.MAIN.ocp_proc) + dstat_comm.read_settings() + + self.entry['R100'].set_text(str( + dstat_comm.settings['r100_trim'][1])) + self.entry['R3k'].set_text(str( + dstat_comm.settings['r3k_trim'][1])) + self.entry['R30k'].set_text(str( + dstat_comm.settings['r30k_trim'][1])) + self.entry['R300k'].set_text(str( + dstat_comm.settings['r300k_trim'][1])) + self.entry['R3M'].set_text(str( + dstat_comm.settings['r3M_trim'][1])) + self.entry['R30M'].set_text(str( + dstat_comm.settings['r30M_trim'][1])) + self.entry['R100M'].set_text(str( + dstat_comm.settings['r100M_trim'][1])) + + __main__.MAIN.start_ocp() + + finally: + for i in self.buttons: + i.set_sensitive(True) def on_write_button_clicked(self, data=None): - __main__.MAIN.on_pot_stop_clicked() - gobject.source_remove(__main__.MAIN.ocp_proc) + for i in self.buttons: + i.set_sensitive(False) + + try: + __main__.MAIN.on_pot_stop_clicked() + gobject.source_remove(__main__.MAIN.ocp_proc) + + dstat_comm.settings['r100_trim'][1] = self.entry['R100'].get_text() + dstat_comm.settings['r3k_trim'][1] = self.entry['R3k'].get_text() + dstat_comm.settings['r30k_trim'][1] = self.entry['R30k'].get_text() + dstat_comm.settings['r300k_trim'][1] = self.entry['R300k'].get_text() + dstat_comm.settings['r3M_trim'][1] = self.entry['R3M'].get_text() + dstat_comm.settings['r30M_trim'][1] = self.entry['R30M'].get_text() + dstat_comm.settings['r100M_trim'][1] = self.entry['R100M'].get_text() + dstat_comm.write_settings() + + __main__.MAIN.start_ocp() + + finally: + for i in self.buttons: + i.set_sensitive(True) + + def on_measure_button_clicked(self, data=None): + if (int(self.entry['time'].get_text()) <= 0 or int(self.entry['time'].get_text()) > 65535): + print "ERR: Time out of range" + return + + for i in self.buttons: + i.set_sensitive(False) + + try: + __main__.MAIN.on_pot_stop_clicked() + gobject.source_remove(__main__.MAIN.ocp_proc) + __main__.MAIN.spinner.start() + + offset = dstat_comm.measure_offset(self.get_params()['time']) + + for i in offset: + print i + print str(-offset[i]) + dstat_comm.settings[i][1] = str(-offset[i]) + + self.entry['R100'].set_text(str( + dstat_comm.settings['r100_trim'][1])) + self.entry['R3k'].set_text(str( + dstat_comm.settings['r3k_trim'][1])) + self.entry['R30k'].set_text(str( + dstat_comm.settings['r30k_trim'][1])) + self.entry['R300k'].set_text(str( + dstat_comm.settings['r300k_trim'][1])) + self.entry['R3M'].set_text(str( + dstat_comm.settings['r3M_trim'][1])) + self.entry['R30M'].set_text(str( + dstat_comm.settings['r30M_trim'][1])) + self.entry['R100M'].set_text(str( + dstat_comm.settings['r100M_trim'][1])) + __main__.MAIN.start_ocp() - dstat_comm.settings['r100_trim'][1] = self.entry['R100'].get_text() - dstat_comm.settings['r3k_trim'][1] = self.entry['R3k'].get_text() - dstat_comm.settings['r30k_trim'][1] = self.entry['R30k'].get_text() - dstat_comm.settings['r300k_trim'][1] = self.entry['R300k'].get_text() - dstat_comm.settings['r3M_trim'][1] = self.entry['R3M'].get_text() - dstat_comm.settings['r30M_trim'][1] = self.entry['R30M'].get_text() - dstat_comm.settings['r100M_trim'][1] = self.entry['R100M'].get_text() - dstat_comm.write_settings() - - __main__.MAIN.start_ocp() \ No newline at end of file + finally: + for i in self.buttons: + i.set_sensitive(True) + __main__.MAIN.spinner.stop() \ No newline at end of file diff --git a/dstat_interface/__main__.py b/dstat_interface/main.py similarity index 99% rename from dstat_interface/__main__.py rename to dstat_interface/main.py index 64619738042a501d408c4ea985c2d4b629630cec..fbf795cb200cdcd80f0a681415607a862497444f 100755 --- a/dstat_interface/__main__.py +++ b/dstat_interface/main.py @@ -731,6 +731,8 @@ class Main(object): for i in self.current_exp.commands: self.rawbuffer.insert_at_cursor(i) + self.rawbuffer.insert_at_cursor("\n") + for col in zip(*self.current_exp.data): for row in col: self.rawbuffer.insert_at_cursor(str(row)+ " ") @@ -769,6 +771,9 @@ class Main(object): comm.serial_instance.ctrl_pipe_p.send('a') while not (comm.serial_instance.proc_pipe_p.recv() == "ABORT"): pass + + while comm.serial_instance.data_pipe_p.poll(): + comm.serial_instance.data_pipe_p.recv() except AttributeError: pass except: