class BrownianMotionVisualization(HasTraits): scene = Instance(MlabSceneModel, ()) plot = Instance(PipelineBase) x = Array() y = Array() z = Array() timer = Any() timer_button = Button() @on_trait_change('scene.activated') def create_plot(self): """ Wait before the scene is activated before trying to add data, modules, filers into it. """ x, y, z = compute_positions() self.plot = self.scene.mlab.points3d(x, y, z) def _timer_button_fired(self): if self.timer is None: self.timer = Timer(100, self.update_plot) else: self.timer.stop() self.timer = None def update_plot(self): """ Recompute the ball positions and redraw """ x, y, z = compute_positions() self.plot.mlab_source.set(x=x, y=y, z=z) # the layout of the dialog created view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene), height=250, width=300, show_label=False), Item('timer_button', label = "Start/Stop animation", show_label = False), )
class PhysiologyController(Controller): buffer_ = Any iface_physiology = Any buffer_raw = Any buffer_proc = Any buffer_ts = Any buffer_ttl = Any physiology_ttl_pipeline = Any buffer_spikes = List(Any) state = Enum('master', 'client') process = Instance('tdt.DSPProject') timer = Instance(Timer) parent = Any shell_variables = Dict # These define what variables will be available in the Python shell. Right # now we can't add various stuff such as the data and interface classes # because they have not been created yet. I'm not sure how we can update # the Python shell with new instances once the experiment has started # running. def _shell_variables_default(self): return dict(controller=self, c=self) def init(self, info): self.setup_physiology() self.model = info.object if self.state == 'master': self.process.start() self.iface_physiology.trigger('A', 'high') self.start() def close(self, info, is_ok, from_parent=False): return True if self.state == 'client': return from_parent else: # The function confirm returns an integer that represents the # response that the user requested. YES is a constant (also # imported from the same module as confirm) corresponding to the # return value of confirm when the user presses the "yes" button on # the dialog. If any other button (e.g. "no", "abort", etc.) is # pressed, the return value will be something other than YES and we # will assume that the user has requested not to quit the # experiment. if confirm(info.ui.control, 'OK to stop?') == YES: self.stop(info) return True else: return False def setup_physiology(self): # Load the circuit circuit = join(get_config('RCX_ROOT'), 'physiology') self.iface_physiology = self.process.load_circuit(circuit, 'RZ5') # Initialize the buffers that will be spooling the data self.buffer_raw = self.iface_physiology.get_buffer('craw', 'r', src_type='float32', dest_type='float32', channels=CHANNELS, block_size=1048) self.buffer_filt = self.iface_physiology.get_buffer('cfilt', 'r', src_type='int16', dest_type='float32', channels=CHANNELS, block_size=1048) self.buffer_ts = self.iface_physiology.get_buffer('trig/', 'r', src_type='int32', dest_type='int32', block_size=1) self.buffer_ts_start = self.iface_physiology.get_buffer('trig/', 'r', src_type='int32', dest_type='int32', block_size=1) self.buffer_ts_end = self.iface_physiology.get_buffer('trig\\', 'r', src_type='int32', dest_type='int32', block_size=1) self.buffer_ttl = self.iface_physiology.get_buffer('TTL', 'r', src_type='int8', dest_type='int8', block_size=1) for i in range(CHANNELS): name = 'spike{}'.format(i+1) buffer = self.iface_physiology.get_buffer(name, 'r', block_size=SPIKE_SNIPPET_SIZE+2) self.buffer_spikes.append(buffer) @on_trait_change('model.data') def update_data(self): # Ensure that the data store has the correct sampling frequency for i in range(CHANNELS): data_spikes = self.model.data.spikes for src, dest in zip(self.buffer_spikes, data_spikes): dest.fs = src.fs dest.snippet_size = SPIKE_SNIPPET_SIZE self.model.data.raw.fs = self.buffer_raw.fs self.model.data.processed.fs = self.buffer_filt.fs self.model.data.ts.fs = self.iface_physiology.fs self.model.data.epoch.fs = self.iface_physiology.fs self.model.data.sweep.fs = self.buffer_ttl.fs # Setup the pipeline targets = [self.model.data.sweep] self.physiology_ttl_pipeline = deinterleave_bits(targets) def start(self): self.timer = Timer(100, self.monitor_physiology) def stop(self): self.timer.stop() self.process.stop() def monitor_physiology(self): # Acquire raw physiology data waveform = self.buffer_raw.read() self.model.data.raw.send(waveform) # Acquire filtered physiology data waveform = self.buffer_filt.read() self.model.data.processed.send(waveform) # Acquire sweep data ttl = self.buffer_ttl.read() self.physiology_ttl_pipeline.send(ttl) # Get the timestamps ts = self.buffer_ts.read().ravel() self.model.data.ts.send(ts) ends = self.buffer_ts_end.read().ravel() starts = self.buffer_ts_start.read(len(ends)).ravel() self.model.data.epoch.send(zip(starts, ends)) # Get the spikes. Each channel has a separate buffer for the spikes # detected online. Need to add 2 to the snippet size to compensate for # the extra samples provided with the snippet (the timestamp and the # classifier). snippet_shape = (-1, SPIKE_SNIPPET_SIZE+2) for i in range(CHANNELS): data = self.buffer_spikes[i].read().reshape(snippet_shape) # First sample of each snippet is the timestamp (as a 32 bit # integer) and last sample is the classifier (also as a 32 bit # integer). The bits in between should be interpreted as 32-bit # floating point values. snip = data[:,1:-1] ts = data[:,0].view('int32') cl = data[:,-1].view('int32') self.model.data.spikes[i].send(snip, ts, cl) @on_trait_change('model.settings.spike_signs') def set_spike_signs(self, value): for ch, sign in enumerate(value): name = 's_spike{}'.format(ch+1) self.iface_physiology.set_tag(name, sign) @on_trait_change('model.settings.spike_thresholds') def set_spike_thresholds(self, value): for ch, threshold in enumerate(value): name = 'a_spike{}'.format(ch+1) self.iface_physiology.set_tag(name, threshold) @on_trait_change('model.settings.monitor_fc_highpass') def set_monitor_fc_highpass(self, value): self.iface_physiology.set_tag('FiltHP', value) @on_trait_change('model.settings.monitor_fc_lowpass') def set_monitor_fc_lowpass(self, value): self.iface_physiology.set_tag('FiltLP', value) @on_trait_change('model.settings.monitor_ch_1') def set_monitor_ch_1(self, value): self.iface_physiology.set_tag('ch1_out', value) @on_trait_change('model.settings.monitor_ch_2') def set_monitor_ch_2(self, value): self.iface_physiology.set_tag('ch2_out', value) @on_trait_change('model.settings.monitor_ch_3') def set_monitor_ch_3(self, value): self.iface_physiology.set_tag('ch3_out', value) @on_trait_change('model.settings.monitor_gain_1') def set_monitor_gain_1(self, value): self.iface_physiology.set_tag('ch1_out_sf', value*1e3) @on_trait_change('model.settings.monitor_gain_2') def set_monitor_gain_2(self, value): self.iface_physiology.set_tag('ch2_out_sf', value*1e3) @on_trait_change('model.settings.monitor_gain_3') def set_monitor_gain_3(self, value): self.iface_physiology.set_tag('ch3_out_sf', value*1e3) @on_trait_change('model.settings.diff_matrix') def set_diff_matrix(self, value): self.iface_physiology.set_coefficients('diff_map', value.ravel()) def load_settings(self, info): instance = load_instance(PHYSIOLOGY_ROOT, PHYSIOLOGY_WILDCARD) if instance is not None: self.model.settings.copy_traits(instance) def saveas_settings(self, info): dump_instance(self.model.settings, PHYSIOLOGY_ROOT, PHYSIOLOGY_WILDCARD) @on_trait_change('model.channel_mode') def _channel_mode_changed(self, new): if new == 'TDT': offset = 0 elif new =='TBSI': offset = 1 else: offset = 2 self.iface_physiology.set_tag('ch_offset', offset*16+1)
class LogFilePlots(traits.HasTraits): """just a several tabbed view of several log file plot objects""" lfps = traits.List( plotObjects.logFilePlot.LogFilePlot) #list of possible fits selectedLFP = traits.Instance(plotObjects.logFilePlot.LogFilePlot) #logFilePlotGroup = traitsui.Group(traitsui.Item("logFilePlotObject", editor = traitsui.InstanceEditor(), style="custom", show_label=False),label="Log File Plotter") logFilePlotsGroup = traitsui.Group(traitsui.Item( 'lfps', style="custom", editor=traitsui.ListEditor(use_notebook=True, deletable=True, selected="selectedLFP", export='DockWindowShell', page_name=".logFilePlotsTabName"), label="logFilePlots", show_label=False), springy=True) autoRefreshTimer = traits.Instance(Timer) autoRefreshDialog = traits.Instance( plotObjects.autoRefreshDialog.AutoRefreshDialog) #for saving and loading default_directory = os.path.join('N:', os.sep, 'Data', 'eagleLogs') file_wildcard = traits.String("CSV Master Settings (*.csv)|All files|*") menubar = traitsmenu.MenuBar( traitsmenu.Menu(traitsui.Action(name='Add Log File Plot', action='_add_lfp'), traitsui.Action(name='Clone selected', action='_add_with_current_lfp'), traitsui.Action(name='Print current', action='_print_current'), traitsui.Action(name='Auto refresh', action='_autoRefresh_dialog'), traitsui.Action(name='Matplotlibify', action='_matplotlibify_dialog'), traitsui.Action(name='Save as', action='_save_as_settings'), traitsui.Action(name='Load as', action='_open_settings'), name="Menu"), ) statusBarString = traits.String() traits_view = traitsui.View(logFilePlotsGroup, title="Log File Plots", statusbar="statusBarString", icon=pyface.image_resource.ImageResource( os.path.join('icons', 'eagles.ico')), resizable=True, menubar=menubar) def __init__(self, N, **traitsDict): """N is initial number of logFilePlots """ super(traits.HasTraits, self).__init__(**traitsDict) self.lfps = [ plotObjects.logFilePlot.LogFilePlot() for c in range(0, N) ] self.selectedLFP = self.lfps[0] for lfp, counter in zip(self.lfps, range(0, N)): lfp.logFilePlotsBool = True lfp.logFilePlotsTabName = "log file plot " + str(counter) lfp.logFilePlotsReference = self def _add_lfp(self): """called from menu. adds a new logfileplot to list and hence to gui in case you run out """ new = plotObjects.logFilePlot.LogFilePlot() new.logFilePlotsBool = True new.logFilePlotsReference = self self.lfps.append(new) def _add_with_current_lfp(self): """called from menu. adds a new logfileplot to list and hence to gui in case you run out """ cloneTraits = [ "logFile", "mode", "masterList", "xAxis", "yAxis", "aggregateAxis", "series", "xLogScale", "yLogScale", "interpretAsTimeAxis", "filterYs", "filterMinYs", "filterMaxYs", "filterXs", "filterMinXs", "filterMaxXs", "filterNaN", "logFilePlotsBool" ] new = self.selectedLFP.clone_traits(traits=cloneTraits, copy="deep") new.logFilePlotsReference = self new.__init__() self.lfps.append(new) def _print_current(self): """opens matplotlibify that allows user to save to one note or print the image """ self.selectedLFP._savePlotButton_fired() def _autoRefresh_dialog(self): """when user clicks autorefresh in the menu this function calls the dialog after user hits ok, it makes the choices of how to setup or stop the timer""" logger.info("auto refresh dialog called") if self.autoRefreshDialog is None: self.autoRefreshDialog = plotObjects.autoRefreshDialog.AutoRefreshDialog( ) self.autoRefreshDialog.configure_traits() logger.info("dialog edit traits finished") if self.autoRefreshDialog.autoRefreshBool: if self.autoRefreshTimer is not None: self.autoRefreshTimer.stop() self.selectedLFP.autoRefreshObject = self.autoRefreshDialog #this gives it all the info it needs self.autoRefreshTimer = Timer( self.autoRefreshDialog.minutes * 60.0 * 1000.0, self.selectedLFP.autoRefresh) logger.info("started auto refresh timer to autorefresh") else: self.selectedLFP.autoRefreshObject = None logger.info("stopping auto refresh") if self.autoRefreshTimer is not None: self.autoRefreshTimer.stop() def _matplotlibify_dialog(self): import matplotlibify.matplotlibify dialog = matplotlibify.matplotlibify.Matplotlibify( logFilePlotReference=self.selectedLFP, logFilePlotsReference=self) dialog.configure_traits() def _save_settings(self, settingsFile): logger.debug("_save_settings call saving settings") with open(settingsFile, 'wb') as sfile: writer = csv.writer(sfile) writer.writerow([-1, 'N', len(self.lfps)]) c = 0 lfp_traits = [ 'mode', 'logFilePlotBool', 'fitLogFileBool', 'autoFitWithRefresh', 'softRefresh', 'errorBarMode', 'logFile', 'xAxis', 'yAxis', 'aggregateAxis', 'masterList', 'series', 'xLogScale', 'yLogScale', 'interpretAsTimeAxis', 'filterYs', 'filterMinYs', 'filterMaxYs', 'filterXs', 'filterMinXs', 'filterMaxXs', 'filterNaN', 'filterSpecific', 'filterSpecificString', 'filterVariableSelector', 'logFilePlotsBool', 'logFilePlotsTabName' ] for lfp in self.lfps: for trait in lfp_traits: try: writer.writerow([ c, trait, getattr(lfp, trait), type(getattr(lfp, trait)) ]) except Exception as e: logger.error(e.message) c += 1 def _load_settings(self, settingsFile): load_df = pandas.read_csv( settingsFile, na_filter=False, header=None, names=['lfp_idx', 'variable', 'value', 'type']) global_settings_df = load_df[load_df['lfp_idx'] == -1] N = int( global_settings_df[global_settings_df['variable'] == 'N'].value[0]) self.__init__(N) for idx in range(0, N): df = load_df[load_df['lfp_idx'] == idx] traits = list(df.variable.values) values = list(df.value.values) types = list(df.type.values) traitsDict = { trait: (value, type_val) for (trait, value, type_val) in zip(traits, values, types) } logger.info("loading lfp %G" % idx) logger.info("dictionary %s" % traitsDict) self.lfps[idx].load_settings(traitsDict) def _save_as_settings(self): """Called when user presses save as in Menu bar """ dialog = FileDialog(action="save as", default_directory=self.default_directory, wildcard=self.file_wildcard) dialog.open() if dialog.return_code == OK: self.settingsFile = dialog.path if self.settingsFile[-4:] != ".csv": self.settingsFile += ".csv" self._save_settings(self.settingsFile) def _open_settings(self): logger.debug("_open_settings call . Opening a settings file") dialog = FileDialog(action="open", default_directory=self.default_directory, wildcard=self.file_wildcard) dialog.open() if dialog.return_code == OK: self.settingsFile = dialog.path self._load_settings(self.settingsFile)
class EagleHandler(traitsui.Handler): #--------------------------------------------------------------------------- # State traits #--------------------------------------------------------------------------- model = traits.Instance(CameraImage) view = traits.Any watchFolderTimer = traits.Instance(Timer) #--------------------------------------------------------------------------- # Handler interface #--------------------------------------------------------------------------- def closed(self, info, is_ok): """ Handles a dialog-based user interface being closed by the user. Overridden here to stop the timer once the window is destroyed. """ try: #stop any previous timer, should only have 1 timer at a time logger.info("closing image plot inspector") except Exception as e: logger.error("couldn't close: error: %s " % e.message) return def init(self, info): self.view = info.object self.model = info.object.model self.model.on_trait_change(self._model_changed, "model_changed") self.view.boxSelection2D.on_trait_change(self._box_selection_complete, "selection_complete") self.view.lineInspectorX.on_trait_change(self._setCentreGuess, "mouseClickEvent") self.view.on_trait_change(self._watchFolderModeChanged, "watchFolderBool") #self.start_timer() def _model_changed(self): if self.view is not None: self.view.update(self.model) def _box_selection_complete(self): logger.critical("Box selection complete") [ self.view.selectedFit.startX, self.view.selectedFit.startY, self.view.selectedFit.endX, self.view.selectedFit.endY ] = map(int, self.view.boxSelection2D._get_coordinate_box()) logger.debug("box selection results %s " % [ self.view.selectedFit.startX, self.view.selectedFit.startY, self.view.selectedFit.endX, self.view.selectedFit.endY ]) def _setCentreGuess(self): x_ndx, y_ndx = self.view._image_index.metadata["selections"] logger.debug("selected fit %s " % self.view.selectedFit) #x0=self.model.xs[x_ndx] #y0=self.model.ys[y_ndx] #print x0,y0 #for variable in self.view.selectedFit self.view.selectedFit.x0.initialValue = self.model.xs[x_ndx] self.view.selectedFit.y0.initialValue = self.model.ys[y_ndx] def _watchFolderModeChanged(self): eagle = self.view eagle.oldFiles = [] if eagle.watchFolderBool: if eagle.watchFolder == '' and os.path.isfile(eagle.selectedFile): eagle.watchFolder = os.path.dirname(eagle.selectedFile) eagle.oldFiles = self.getPNGFiles() self.watchFolderTimer = Timer(2000., self.checkForNewFiles) else: if self.watchFolderTimer is not None: self.watchFolderTimer.stop() self.watchFolderTimer = None def getPNGFiles(self): """return set of all pngs in watched folder """ eagle = self.view return set([ f for f in os.listdir(eagle.watchFolder) if os.path.splitext(f)[1] == ".png" ]) def parseFiles(self, filesSet): """given a set of files, this file will check how many match the search string and return the LIST of all those that do""" searchString = self.view.searchString return [fileName for fileName in filesSet if searchString in fileName] def checkForNewFiles(self): """function called by timer thread. Checks directory for new files and then checks if they match searchString (calls parseFiles)""" eagle = self.view if not os.path.isdir(eagle.watchFolder): logger.warning( "watchFolder is not a valid directory. Will not check for new files" ) return currentFiles = self.getPNGFiles() newFiles = currentFiles - eagle.oldFiles if len(newFiles) == 0: #logger.debug("No new files detected in watch Directory") return else: logger.debug("new files = %s" % list(newFiles)) newFiles = self.parseFiles(newFiles) logger.debug("new files after checking for sub-string = %s" % list(newFiles)) if len(newFiles) == 0: logger.info( "new files were found but none matched search string %s" % eagle.searchString) return if len(newFiles) > 1: logger.warning( "more than one new file found %s only analysing first one" % newFiles) newFile = newFiles[0] logger.debug("new file is %s" % newFile) logger.debug("new full path file is %s" % os.path.join(eagle.watchFolder, newFile)) eagle.selectedFile = os.path.join(eagle.watchFolder, newFile) eagle.oldFiles = currentFiles
class DACChannel(traits.HasTraits): def __init__(self, setchannelName, port, connection, rpiADC): self.channelName = setchannelName self.x = ad.ADEvalBC() self.port = port self.connection = connection self.rpiADC = rpiADC self.wmLockTimer = Timer(1000, self.wmLock2) self.wmLockTimer.Stop() # time.sleep(5) # self.start_timer() update = traits.Button() set = traits.Button() pinMode = '0' relockMode = traits.Enum("Manual Mode", "Doubling cavity Relock", "Wavemeter Relock", "Wavemeter Lock") #set_Relock = traits.Button() #---- MANUAL MODE ----# voltage = traits.Float(desc="Voltage of the Channel") setVoltage = traits.Float(desc="set Voltage") powerModeMult = 0 channelName = traits.Str(desc="Name") channelDescription = traits.Str(desc="Description") powerMode = traits.Enum( 5, 10, 10.8, desc="power Mode", ) bipolar = traits.Bool(desc="Bipolarity") channelMessage = traits.Str() bytecode = traits.Str() port = traits.Str() def pinSet(self, channel, mode): cmd = "pin=" + channel + mode self.connection.send(cmd) #print cmd def _update_fired(self): if self.bipolar == True: a = "bipolar" bip = True self.powerModeMult = -1 else: a = "unipolar" bip = False self.powerModeMult = 0 cmd = "cmd=" + self.x.setMaxValue(self.port, self.powerMode, bip) #print cmd self.connection.send(cmd) b = "Mode set to %.1f" % self.powerMode self.channelMessage = b + ' ' + a self._set_fired() def _set_fired(self): if ((self.setVoltage > self.powerMode) and (self.bipolar == False)): print "setVoltage out of bounds. Not sending." elif ((abs(self.setVoltage) > self.powerMode) and (self.bipolar == True)): print "setVoltage out of bounds. Not sending." else: cmd = "cmd=" + self.x.generate_voltage( self.port, self.powerMode, self.powerModeMult * self.powerMode, self.setVoltage) self.connection.send(cmd) self.bytecode = self.x.generate_voltage( self.port, self.powerMode, self.powerModeMult * self.powerMode, self.setVoltage) self.voltage = self.setVoltage #---- MANUAL MODE GUI ----# voltageGroup = traitsui.HGroup(traitsui.VGroup( traitsui.Item('voltage', label="Measured Voltage", style_sheet='* { font-size: 18px; }', style="readonly"), traitsui.Item('setVoltage', label="Set Value"), ), traitsui.Item('set', show_label=False), show_border=True) powerGroup = traitsui.VGroup(traitsui.HGroup( traitsui.Item('powerMode', label="Power Mode"), traitsui.Item('bipolar'), ), traitsui.HGroup( traitsui.Item('update', show_label=False), traitsui.Item('channelMessage', show_label=False, style="readonly"), ), traitsui.Item('bytecode', show_label=False, style="readonly"), traitsui.Item('switch_Lock'), show_border=True) manualGroup = traitsui.VGroup(traitsui.Item('channelName', label="Channel Name", style="readonly"), voltageGroup, show_border=True, visible_when='relockMode == "Manual Mode"') #---- DOUBLING CAVITY MODE GUI----# adcChannel = traits.Enum(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, desc="Channel of the rpiADC") adcVoltage = traits.Float(desc="Voltage on rpiADC Channel") DCscan_and_lock = traits.Button() switch_Lock = traits.Button() DCconnect = traits.Bool() DCautolock = traits.Button() DCadcVoltages = None DCadcVoltagesMean = None DCtolerance = 0.01 #Volt DCmistakeCounter = 0 DCminBoundary = traits.Float() DCmaxBoundary = traits.Float() DCnoOfSteps = traits.Int() #Updates voltage of the selected channel" def _adcVoltage_update(self): self.adcVoltage = self._adcVoltage_get() #Gets voltage of the selected channel via rpiADC Client def _adcVoltage_get(self): return self.rpiADC.getResults()[self.adcChannel] # print "latest results = %s " % self.rpiADC.latestResults # self.rpiADC.getResults() # print "latest results = %s " % self.rpiADC.latestResults # if self.adcChannel in self.rpiADC.latestResults: # return self.rpiADC.latestResults[self.adcChannel] # else: # return -999 #As soon as the connect button is checked, automatic updating of the adc voltage is initiated. #When it is unchecked, the update stops def _DCconnect_changed(self): if self.DCconnect == True: self._start_PD_timer() else: self.PDtimer.stop() self.adcVoltage = -999 print "PD timer stopped." #Starts the timer, that only updates the displayed voltage def _start_PD_timer(self): self.PDtimer = Timer(1000.0, self._adcVoltage_update) #Starts a timer, that updates the displayed voltage, as well as does the "in lock" checking def _start_PD_lock_timer(self): self.PDtimer = Timer(1000.0, self.update_PD_and_Lock) #Controls if everything is still in lock. Also updates displayed voltage. It counts still in lock, when the measured frequency #is within DC tolerance of the mean of the last five measured frequencies. def update_PD_and_Lock(self): self._adcVoltage_update() #still display Voltage pdVoltage = self._adcVoltage_get() #print "Updated Frequency" #mistakeCounter = 0 if len(self.DCadcVoltages ) < 5: #number of Measurements that will be compared print "Getting Data for Lock. Do not unlock!" self.DCadcVoltages = np.append(self.DCadcVoltages, pdVoltage) else: self.DCadcVoltagesMean = np.mean( self.DCadcVoltages) #Mean Frequency to compare to if (abs(pdVoltage - self.DCadcVoltagesMean) < self.DCtolerance): self.DCadcVoltages = np.append(self.DCadcVoltages, pdVoltage) self.DCadcVoltages = np.delete( self.DCadcVoltages, 0) #keep Array at constant length print "Still locked." if self.DCmistakeCounter > 0: self.DCmistakeCounter = 0 else: self.DCmistakeCounter += 1 if self.DCmistakeCounter > 5: self.PDtimer.stop() self._start_PD_timer() #keep Frequency on display.. self._DCscan_and_lock_fired() self._DCautolock_fired() else: print self.DCmistakeCounter #This button is used, when everything is already locked. It prepares the voltage mean array, stops the PD timer and starts the update_PD_and_Lock timer routine def _DCautolock_fired(self): if self.DCconnect == True: self.DCadcVoltages = np.array([]) self.PDtimer.stop() self._start_PD_lock_timer() else: print "No adcRPi connected." #This function (button) scans the voltage and reads the RPiADC voltage of the selected channel. It subsequently attempts #to lock the Cavity at the voltage, where the RPiADC voltage is highest. The Algorithm for the lock is as follows: #1) It does a coarse DAC voltage scan with the parameters selected in the input (minBoundary etc). This scan is terminated when # the adc voltage goes above a threshold (3.2 V - hardcoded, maybe add input), or after scanning the whole range. #2) A fine scan 0.3V(dac) around the maximum (or around the 3.2V dac voltage) is done. Currently the number of steps is hardcoded to 600, # which worked well. This scan terminated either by going to a 3.3V (adc) threshhold or after finishing. #3) The Cavity is locked at a dac voltage that corresponds to either the maximum after the whole scan or the 3.3V threshold. def _DCscan_and_lock_fired(self): self.pinSet(self.port, '1') #Unlock time.sleep( 1 ) #If the cavity was in lock before we can not directly start scanning, or else it will read 3.3V as first value voltages = np.linspace( self.DCminBoundary, self.DCmaxBoundary, self.DCnoOfSteps) #Parameters for first scan set by GUI diodeVoltages = np.array([]) for entry in voltages: #First scan self.setVoltage = entry self._set_fired() #update setVoltage diodeVoltages = np.append(diodeVoltages, self._adcVoltage_get()) volt = self._adcVoltage_get() print volt if volt >= 3.2: #Threshold for first scan break time.sleep( 0.05 ) #sleep time between every step for adc readout, maybe it is possible to make the scan faster self.setVoltage = voltages[diodeVoltages.argmax( axis=0)] #DAC Voltage corresponding to the highest adc voltage print "Attempting to reduce scan Range" print self.setVoltage time.sleep(2.0) if self.setVoltage < 0.3: #make sure we do not scan below zero, as the lock box does not like negative voltages. auxSetVolt = 0 else: auxSetVolt = self.setVoltage - 0.3 voltages = np.linspace(auxSetVolt, self.setVoltage + 0.3, 600) #parameters for second scan diodeVoltages = np.array([]) for entry in voltages: #Second scan if entry > self.powerMode: #Our voltage can not go above our maximum value break self.setVoltage = entry self._set_fired() diodeVoltages = np.append(diodeVoltages, self._adcVoltage_get()) volt = self._adcVoltage_get() print volt if volt >= 3.3: #threshold for second scan print self.setVoltage self.pinSet(self.port, '0') return time.sleep(0.1) self.setVoltage = voltages[diodeVoltages.argmax(axis=0)] print "DAC Voltage set to %f" % voltages[diodeVoltages.argmax(axis=0)] print ".. this corresponds to a diode Voltage of %f" % diodeVoltages[ diodeVoltages.argmax(axis=0)] self._set_fired() time.sleep(0.2) self.pinSet(self.port, '0') #Lock print "Voltage set to %f to attempt relock." % self.setVoltage return #Changes from lock to dither and other way around def _switch_Lock_fired(self): if self.pinMode == '0': self.pinMode = '1' self.pinSet(self.port, self.pinMode) else: self.pinMode = '0' self.pinSet(self.port, self.pinMode) #Gui Stuff# DCgroup = traitsui.VGroup( traitsui.HGroup( traitsui.VGroup( traitsui.Item('adcChannel'), traitsui.Item('adcVoltage', style='readonly'), ), traitsui.VGroup( traitsui.Item('DCmaxBoundary'), traitsui.Item('DCminBoundary'), traitsui.Item('DCnoOfSteps'), ), ), #traitsui.Item('switch_Lock'), traitsui.HGroup( traitsui.VGroup( traitsui.Item('DCscan_and_lock'), traitsui.Item('DCautolock'), ), traitsui.Item('DCconnect')), visible_when='relockMode == "Doubling cavity Relock"', show_border=True, ) #---- WAVEMETER GUI ----# wavemeter = traits.Enum("Humphry", "Ion Cavity") wmChannel = traits.Enum(1, 2, 3, 4, 5, 6, 7, 8) wmFrequency = traits.Float() wmConnected = traits.Bool() wmHWI = None wmIP = None wmPort = None wmReLock = traits.Button() wmFrequencyLog = np.array([]) wmMeanFrequency = None wmTolerance = 0.0000006 #Frequency tolerance in THz, set to 60 MHz in accordance with wm accuracy mistakeCounter = 0 #wmPolarity = traits.Enum("+", "-") wmEmptyMemory = traits.Button() #When hitting wmConnected, a connection to the wavemeter and readout is established def _wmConnected_changed(self): if self.wmConnected == True: if self.wavemeter == "Humphry": self.wmIP = '192.168.0.111' self.wmPort = 6101 elif self.wavemeter == "Ion Cavity": self.wmIP = '192.168.32.2' self.wmPort = 6100 self.wmHWI = PyHWI.DECADSClientConnection('WLM', self.wmIP, self.wmPort) #frequencyArray = wmHWI.getFrequency(True, self.wmChannel) self.start_timer() else: self.wmFrequency = -999 self.timer.Stop() #stops either lock_timer or read_timer. print "Timer stopped" #GUI stuff wmGroup = traitsui.VGroup(traitsui.Item('wmFrequency', style='readonly'), traitsui.Item('wmConnected'), traitsui.Item('wmReLock'), traitsui.HGroup( traitsui.Item('wavemeter'), traitsui.Item('wmChannel'), traitsui.Item('wmEmptyMemory', show_label=False)), visible_when='relockMode == "Wavemeter Relock"') #Resets the memory of the lock (the array which saves the last read frequencies) def _wmEmptyMemory_fired(self): self.wmFrequencyLog = np.array([]) print "Memory empty." #starts readout-only timer def start_timer(self): print "Timer started" self.timer = Timer(1000.0, self.update_wm) #starts readout-and-lock timer, analogue to the doubling cavity case def start_lock_timer(self): print "Lock timer started" self.timer = Timer(1000.0, self.update_wm_and_lock) #updates the displayed frequency def update_wm(self): frequencyArray = self.wmHWI.getFrequency(True, self.wmChannel) self.wmFrequency = frequencyArray[1] #updates frequency and checks if its still near the mean of the last five (hardcoded, see below) measured frequencies. If not, it attempts a relock. def update_wm_and_lock(self): self.update_wm() frequencyArray = self.wmHWI.getFrequency(True, self.wmChannel) #print "Updated Frequency" #mistakeCounter = 0 if len(self.wmFrequencyLog ) < 5: #number of Measurements that will be compared print "Getting Data for Lock. Do not unlock!" self.wmFrequencyLog = np.append(self.wmFrequencyLog, frequencyArray[1]) else: self.wmMeanFrequency = np.mean( self.wmFrequencyLog) #Mean Frequency to compare to if (abs(frequencyArray[1] - self.wmMeanFrequency) < self.wmTolerance): self.wmFrequencyLog = np.append(self.wmFrequencyLog, frequencyArray[1]) self.wmFrequencyLog = np.delete( self.wmFrequencyLog, 0) #keep Array at constant length print "Still locked." if self.mistakeCounter > 0: self.mistakeCounter = 0 else: self.mistakeCounter += 1 if self.mistakeCounter > 5: #number of measurements that still count as locked, though the frequency is not within boundaries self.timer.stop() self.start_timer() #keep Frequency on display.. self.wmRelock(self.wmMeanFrequency) else: print self.mistakeCounter #Relock procedure. #For now this scans only one time, with a hardcoded number of steps. It might be worth modifying this to look like the doubling cavity relock procedure. def wmRelock(self, wantedFrequency): self.pinSet(self.port, '1') voltages = np.linspace(self.powerModeMult * self.powerMode, self.powerMode, 10) wmRelockTry = 0 try: while (wmRelockTry < 5): #attempt relock five times for entry in voltages: self.setVoltage = entry self._set_fired() time.sleep(1.0) frequencyArray = self.wmHWI.getFrequency( True, self.wmChannel) if (abs(frequencyArray[1] - wantedFrequency) < self.wmTolerance): print "Relock_attempt!" self.pinSet(self.port, '0') self._wmLock_fired() raise GetOutOfLoop #Opens the function again (inductively). Maybe fix that by going back #to the level above somehow. wmRelockTry += 1 print "Relock try %f not succesful" % wmRelockTry print "Was not able to Relock." except GetOutOfLoop: print "gotOutOfLoop" pass def _wmReLock_fired(self): #self.wmFrequencyLog = np.array([]) self.mistakeCounter = 0 if self.wmConnected == True: self.timer.Stop( ) #Switch from read-only timer to read-and-log timer. If both run at the same time self.start_lock_timer() #we run into timing problems. else: print "No Wavemeter connected!" print "hi" #---- WAVEMETERLOCK - DE 17092018 GUI ----# wavemeter = traits.Enum("Humphry", "Ion Cavity") wmChannel = traits.Enum(1, 2, 3, 4, 5, 6, 7, 8) wmFrequency = traits.Float() wmConnected = traits.Bool() wmHWI = None wmIP = None wmPort = None isRunning = traits.Bool(False) wmVoltage = 0 wmLockTimer = traits.Instance(Timer) wmLockStart = traits.Button() wmLockStop = traits.Button() wmFrequencyLog = np.array([]) wmMeanFrequency = None wmTargetFrequency = traits.Float() #frequency to lock wmGain = traits.Float() #Gain for wavemeterlock wmTolerance = 0.0000006 #Frequency tolerance in THz, set to 60 MHz in accordance with wm accuracy mistakeCounter = 0 #wmPolarity = traits.Enum("+", "-") wmEmptyMemory = traits.Button() #GUI stuff wmlockGroup = traitsui.VGroup( traitsui.HGroup(traitsui.Item('wavemeter'), traitsui.Item('wmChannel'), traitsui.Item('wmEmptyMemory', show_label=False)), traitsui.Item('wmFrequency', style='readonly'), traitsui.Item('wmConnected'), traitsui.Item('wmTargetFrequency'), traitsui.Item('wmGain'), traitsui.HGroup( traitsui.Item('wmLockStart', visible_when="isRunning == False"), traitsui.Item('wmLockStop', visible_when="isRunning == True")), visible_when='relockMode == "Wavemeter Lock"') def _wmLockStart_fired(self): print "Start: Wavemeterlock" self.isRunning = True self.wmLockTimer.Start() def wmLock2(self): # Calculate error in MHz error = (self.wmFrequency - self.wmTargetFrequency) * 10**3 if abs(error) < 5000: self.wmVoltage = self.wmVoltage + error * self.wmGain if self.wmVoltage > 10: self.wmVoltage = 10 elif self.wmVoltage < -10: self.wmVoltage = -10 cmd = "cmd=" + self.x.generate_voltage( self.port, self.powerMode, self.powerModeMult * self.powerMode, self.wmVoltage) self.connection.sendwithoutcomment(cmd) self.bytecode = self.x.generate_voltage( self.port, self.powerMode, self.powerModeMult * self.powerMode, self.wmVoltage) #self.saveToFile( "frequency_data.csv" ) def _wmLockStop_fired(self): print "Stop: Wavemeterlock" self.isRunning = False cmd = "cmd=" + self.x.generate_voltage( self.port, self.powerMode, self.powerModeMult * self.powerMode, 0) #Spannung wieder auf 0 self.connection.sendwithoutcomment(cmd) self.bytecode = self.x.generate_voltage( self.port, self.powerMode, self.powerModeMult * self.powerMode, 0) self.wmVoltage = 0 self.wmLockTimer.Stop() def saveToFile(self, fileName): f = open(fileName, "a") f.write("%f \n" % self.wmFrequency) f.close() #---- PUT TOGETHER CHANNEL ----# selectionGroup = traitsui.HGroup(traitsui.Item('relockMode'), #traitsui.Item('set_Relock'), ) channelGroup = traitsui.VGroup( selectionGroup, powerGroup, wmGroup, wmlockGroup, DCgroup, manualGroup, ) traits_view = traitsui.View(channelGroup)
class TemperatureControlPanel(HasTraits): TIMING_UPDATE_INTERVAL = 500.0 pane_name = Str('Temperature control ') pane_id = Str('sensorscience.unimeas.temperatur_control_pane') output_channels = Dict({0:'temp_controller'}) y_units = Dict({0 : 'temp'}) enable = Bool(False) loop_sequence = Bool(True) poll_timer = Instance(Timer) timing_timer = Instance(Timer) table_entries = List(TableEntry) current_row = Int(0) current_temp = Float actual_temp = Float process_value = Float adjusted_pv = Float PID_out = Float pwm_value = Int max31865_ctrl = Int max31865_error = Int setpoint = Float supply_voltage = Float RT_resistance = Float set_temp = Float current_time = Float row_start_time = Float start_time = Float running = False connected = Bool(False) save_detailed_log = Bool(False) pid_P = Float(0) pid_I = Float(0) pid_D = Float(0) pid_N = Float(0) pwm_table_index = Int(0) use_direct_pwm = Bool(False) pwm_set_direct = Int(0) use_direct_pv = Bool(False) pv_set_direct = Int(0) temp_setpoint = Float resistance_setpoint = Float supply_voltage_setpoint = Float(15.0) RT_resistance_setpoint = Float(109.71) temperature_table = Array selected_com_port = Str selected_pid_configuration = Str available_pid_configurations = List(['Normal TO-8', 'M18 ceramic', 'L-A Cap']) com_ports_list = List(Str) connect = Button connect_button_string = Str('Connect') update_PID_values = Int(0) set_parameters = Button update_pid = Button save = Button load = Button filename = File set_parameters_queue = Instance(Queue.Queue) get_parameters_queue = Instance(Queue.Queue) serial_handler = Instance(SerialHandler) traits_view = View( HGroup(Item('selected_com_port', label='Com port', editor=EnumEditor(name='com_ports_list'), enabled_when='not running'), Item('connect', editor=ButtonEditor(label_value='connect_button_string'), show_label=False, enabled_when='selected_com_port != ""')), Item('enable', label='Enable temp program', enabled_when='connected'), Item('loop_sequence', label='Loop temperature sequence'), Group(Item('table_entries', show_label=False, editor=table_editor, enabled_when='True' ), HGroup(Item('filename'), spring, Item('save', show_label=False), Item('load', show_label=False)), show_border=True, ), Group( HGroup( VGroup(Item('actual_temp', style='readonly', format_str='%.2f'), Item('process_value', style='readonly', format_str='%.2f'), Item('adjusted_pv', style='readonly', format_str='%.2f'), Item('setpoint', style='readonly', format_str='%.2f')), spring, VGroup(Item('pwm_value', style='readonly', format_str='%d'), Item('max31865_ctrl', style='readonly', format_str='%#04x'), Item('max31865_error', style='readonly', format_str='%#04x')), spring, VGroup(Item('PID_out', style='readonly', format_str='%.2f'), Item('supply_voltage', style='readonly', format_str='%.2f'), Item('RT_resistance', style='readonly', format_str='%.2f'))), label='Diagnostic parameters', show_border=True, ), HGroup( Group( VGroup(Item('temp_setpoint', format_str='%.1f')), VGroup(Item('resistance_setpoint', format_str='%.1f')), VGroup(Item('supply_voltage_setpoint', format_str='%.1f')), VGroup(Item('RT_resistance_setpoint', format_str='%.1f')), Item('set_parameters', enabled_when='connected'), label='Adjustable parameters', show_border=True, ), Group( Item('selected_pid_configuration', label='PID adaption', editor=EnumEditor(name='available_pid_configurations'), enabled_when='True'), VGroup(Item('pid_P', label='PID P', format_str='%.7f')), VGroup(Item('pid_I', label='PID I', format_str='%.7f')), VGroup(Item('pid_D', label='PID D', format_str='%.7f')), VGroup(Item('pid_N', label='PID Nd', format_str='%.7f')), VGroup(Item('pwm_table_index', label='tbl_idx')), HGroup(Item('use_direct_pwm'), Item('pwm_set_direct', show_label=False)), HGroup(Item('use_direct_pv'), Item('pv_set_direct', show_label=False)), Item('update_pid', enabled_when='connected'), Item('save_detailed_log', enabled_when='connected'), label='PID parameters - Don\'t touch', show_border=True, ), ), resizable=True, kind='live', handler=TemperatureControlHandler ) def _set_parameters_queue_default(self): return Queue.Queue(QUEUE_LENGHT) def _get_parameters_queue_default(self): return Queue.Queue(QUEUE_LENGHT) def _temp_setpoint_changed(self, new): self.resistance_setpoint = temp_to_resistance(new, self.RT_resistance_setpoint) def _connected_changed(self, new): if new is True: self.connect_button_string = 'Disconnect' self._selected_pid_configuration_changed(self.selected_pid_configuration) self.update_PID_values = 3 else: self.connect_button_string = 'Connect' def _save_detailed_log_changed(self, new): if new: self.fh = open('templog_' + strftime("%y%m%d_%H%M%S") + '.csv', 'w') self.csv_writer = csv.writer(self.fh,quoting=csv.QUOTE_NONNUMERIC) self.csv_writer.writerow(['pv','pid_out','pwm','max31ctrl','max31err','sp', 'sv','RT_ref','pid_p','pid_i','pid_d','pid_n','time']) self.log_start_time = time() else: self.fh.close() def _RT_resistance_setpoint_changed(self, new): self.resistance_setpoint = temp_to_resistance(self.temp_setpoint, new) def _selected_pid_configuration_changed(self, new): self.pid_P = PID_configurations[new]['P'] self.pid_I = PID_configurations[new]['I'] self.pid_D = PID_configurations[new]['D'] self.pid_N = PID_configurations[new]['N'] self.pwm_table_index = PID_configurations[new]['pwm_table_index'] def _selected_pid_configuration_default(self): return 'Normal TO-8' def _available_pid_configurations_default(self): return PID_configurations.keys() def _onTimer(self): self.current_time = time() - self.start_time index = int(np.floor(self.current_time)) if index >= len(self.temperature_table): return if self.temperature_table[index] is not self.current_temp: self.current_temp = self.temperature_table[index] if self.current_row >= len(self.table_entries): return time_left = self.table_entries[self.current_row].time + self.row_start_time - self.current_time self.table_entries[self.current_row].remaining = int(time_left) if self.table_entries[self.current_row].remaining < 1: self.current_row += 1 self.row_changed(time_left) def _poll_queue(self): while not self.get_parameters_queue.empty(): try: values = self.get_parameters_queue.get_nowait() except Queue.Empty: logger.error('No temp recieved from SerialHandler - Strange') pass self.get_parameters_queue.task_done() self.actual_temp = resistance_to_temp(values[0], values[7]) self.process_value = values[0] self.adjusted_pv = values[0]*(109.7/values[7]) self.PID_out = values[1] self.pwm_value = values[2] self.max31856_ctrl = values[3] self.max31856_error = values[4] self.setpoint = values[5] self.supply_voltage = values[6] self.RT_resistance = values[7] if self.update_PID_values > 0: self.update_PID_values -= 1 self.pid_P = values[8] self.pid_I = values[9] self.pid_D = values[10] self.pid_N = values[11] if self.save_detailed_log: self.csv_writer.writerow(values) def _table_entries_default(self): return [TableEntry(time = 15, start_temp = 150, end_temp = 150, remaining = -1), TableEntry(time = 15, start_temp = 350, end_temp = 350, remaining = -1), TableEntry(time = 15, start_temp = 250, end_temp = 250, remaining = -1)] # return [TableEntry(time = 700, start_temp = 50, end_temp = 650, remaining = -1), # TableEntry(time = 200, start_temp = 650, end_temp = 650, remaining = -1), # TableEntry(time = 1200, start_temp = 650, end_temp = 200, remaining = -1), # TableEntry(time = 7300, start_temp = 200, end_temp = 200, remaining = -1), # TableEntry(time = 1200, start_temp = 200, end_temp = 150, remaining = -1), # TableEntry(time = 1200, start_temp = 150, end_temp = 150, remaining = -1), # TableEntry(time = 750, start_temp = 150, end_temp = 650, remaining = -1), # TableEntry(time = 120, start_temp = 650, end_temp = 50, remaining = -1), # TableEntry(time = 700, start_temp = 50, end_temp = 650, remaining = -1), # TableEntry(time = 200, start_temp = 650, end_temp = 650, remaining = -1), # TableEntry(time = 1200, start_temp = 650, end_temp = 300, remaining = -1), # TableEntry(time = 7300, start_temp = 300, end_temp = 300, remaining = -1), # TableEntry(time = 1200, start_temp = 300, end_temp = 150, remaining = -1), # TableEntry(time = 1200, start_temp = 150, end_temp = 150, remaining = -1), # TableEntry(time = 750, start_temp = 150, end_temp = 650, remaining = -1), # TableEntry(time = 120, start_temp = 650, end_temp = 50, remaining = -1), # TableEntry(time = 700, start_temp = 50, end_temp = 650, remaining = -1), # TableEntry(time = 200, start_temp = 650, end_temp = 650, remaining = -1), # TableEntry(time = 1200, start_temp = 650, end_temp = 400, remaining = -1), # TableEntry(time = 7300, start_temp = 400, end_temp = 400, remaining = -1), # TableEntry(time = 1200, start_temp = 400, end_temp = 150, remaining = -1), # TableEntry(time = 1200, start_temp = 150, end_temp = 150, remaining = -1), # TableEntry(time = 750, start_temp = 150, end_temp = 650, remaining = -1), # TableEntry(time = 120, start_temp = 650, end_temp = 50, remaining = -1), # TableEntry(time = 700, start_temp = 50, end_temp = 650, remaining = -1), # TableEntry(time = 200, start_temp = 650, end_temp = 650, remaining = -1), # TableEntry(time = 1200, start_temp = 650, end_temp = 500, remaining = -1), # TableEntry(time = 7300, start_temp = 500, end_temp = 500, remaining = -1), # TableEntry(time = 1200, start_temp = 500, end_temp = 150, remaining = -1), # TableEntry(time = 1200, start_temp = 150, end_temp = 150, remaining = -1), # TableEntry(time = 750, start_temp = 150, end_temp = 650, remaining = -1), # TableEntry(time = 120, start_temp = 650, end_temp = 50, remaining = -1),] def start_stop(self, running): if not self.enable: return if running: if self.running: return self.start_time = time() self.calculate_temperature_table() for row in self.table_entries: row.remaining = row.time self.current_time = 0 self.current_row = 0 self.row_changed(time() - self.start_time) self.current_temp = self.temperature_table[0] self.timing_timer = Timer(self.TIMING_UPDATE_INTERVAL, self._onTimer) else: if not self.running: return if self.timing_timer is not None: self.timing_timer.Stop() self.running = running def add_data(self, data): pass def get_data(self): return {'time': 0}, {'temp': self.actual_temp} def calculate_temperature_table(self): self.temperature_table = np.array([]) for row in self.table_entries: self.temperature_table = np.hstack((self.temperature_table, np.linspace(row.start_temp, row.end_temp, row.time))) def row_changed(self, remainder): self.row_start_time = self.current_time + remainder if self.current_row >= len(self.table_entries): if self.loop_sequence: self.start_time = time() for row in self.table_entries: row.remaining = row.time self.current_time = 0 self.current_row = 0 self.row_changed(time() - self.start_time) def _current_temp_changed(self, new): self.temp_setpoint = new if self.use_direct_pwm: self.pwm_set_direct = int(new) self._update_pid_fired() return self._set_parameters_fired() def _com_ports_list_default(self): import serial.tools.list_ports as lp l = [] for p in lp.grep('Arduino'): l.append(p[0]) return l def _set_parameters_fired(self): self.set_parameters_queue.put_nowait(struct.pack('<c5B5f', 's', 0, 0, 0, 0, self.pwm_table_index, self.resistance_setpoint, self.supply_voltage_setpoint, self.RT_resistance_setpoint, 0, 0)) def _update_pid_fired(self): self.set_parameters_queue.put_nowait(struct.pack('<c4f', 'p', self.pid_P, self.pid_I, self.pid_D, self.pid_N)) self.set_parameters_queue.put_nowait(struct.pack('<c5B5f', 's', 0, int(self.use_direct_pwm), int(self.use_direct_pv), self.pwm_set_direct, self.pwm_table_index, self.resistance_setpoint, self.supply_voltage_setpoint, self.RT_resistance_setpoint, 0, self.pv_set_direct)) self.update_PID_values = 3 def _connect_fired(self): if self.serial_handler is None: self.serial_handler = SerialHandler(self.set_parameters_queue, self.get_parameters_queue, self.selected_com_port) if not self.serial_handler.open_port(): return self.poll_timer = Timer(POLL_INTERVAL, self._poll_queue) self.serial_handler.start() self.connected = True else: self.start_stop(False) self.serial_handler.exit_flag = True self.serial_handler.join() self.serial_handler = None if self.poll_timer is not None: self.poll_timer.stop() self.connected = False def _save_fired(self): if self.filename is '': GenericPopupMessage(message='No filename given').edit_traits() return filehandle = open(self.filename, "w", 1) fieldnames = self.table_entries[0].trait_get().keys() csv_writer = csv.DictWriter(filehandle, fieldnames=fieldnames, delimiter=',', quotechar='"', quoting=csv.QUOTE_NONNUMERIC, lineterminator='\n') csv_writer.writeheader() for row in self.table_entries: csv_writer.writerow(row.trait_get()) filehandle.close() def _load_fired(self): if self.filename is '': GenericPopupMessage(message='No filename given').edit_traits() return filehandle = open(self.filename, 'rU', 1) reader = csv.DictReader(filehandle, lineterminator='\n') self.table_entries = [] for row in reader: self.table_entries.append(TableEntry(time = int(row['time']), start_temp=int(row['start_temp']), end_temp=int(row['end_temp']), remaining=int(row['time']))) filehandle.close() def _set_temp_table_for_calibration(self): pwms = [int(a) for a in np.hstack((np.linspace(0, 255, 256), np.linspace(255, 0, 256)))] self.table_entries = [TableEntry(time = 60, start_temp = t, end_temp = t, remaining = -1) for t in pwms] self.temp_setpoint = self.table_entries[0].start_temp - 1 # Make sure they are unequal